diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-11-02 11:08:28 +0100 |
---|---|---|
committer | Guillaume Martres <smarter@ubuntu.com> | 2016-11-22 01:35:07 +0100 |
commit | 8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch) | |
tree | a8147561d307af862c295cfc8100d271063bb0dd /src/dotty/tools/dotc | |
parent | 6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff) | |
download | dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2 dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip |
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'src/dotty/tools/dotc')
260 files changed, 0 insertions, 75428 deletions
diff --git a/src/dotty/tools/dotc/Bench.scala b/src/dotty/tools/dotc/Bench.scala deleted file mode 100644 index 56b6dabbe..000000000 --- a/src/dotty/tools/dotc/Bench.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ -package dotty.tools -package dotc - -import core.Contexts.Context -import reporting.Reporter - -/** A main class for running compiler benchmarks. Can instantiate a given - * number of compilers and run each (sequentially) a given number of times - * on the same sources. - */ -object Bench extends Driver { - - @sharable private var numRuns = 1 - - def newCompiler(implicit ctx: Context): Compiler = new Compiler - - private def ntimes(n: Int)(op: => Reporter): Reporter = - (emptyReporter /: (0 until n)) ((_, _) => op) - - override def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = - ntimes(numRuns) { - val start = System.nanoTime() - val r = super.doCompile(compiler, fileNames) - println(s"time elapsed: ${(System.nanoTime - start) / 1000000}ms") - r - } - - def extractNumArg(args: Array[String], name: String, default: Int = 1): (Int, Array[String]) = { - val pos = args indexOf name - if (pos < 0) (default, args) - else (args(pos + 1).toInt, (args take pos) ++ (args drop (pos + 2))) - } - - override def process(args: Array[String], rootCtx: Context): Reporter = { - val (numCompilers, args1) = extractNumArg(args, "#compilers") - val (numRuns, args2) = extractNumArg(args1, "#runs") - this.numRuns = numRuns - ntimes(numCompilers)(super.process(args2, rootCtx)) - } -} - - diff --git a/src/dotty/tools/dotc/CompilationUnit.scala b/src/dotty/tools/dotc/CompilationUnit.scala deleted file mode 100644 index 491c2bd9b..000000000 --- a/src/dotty/tools/dotc/CompilationUnit.scala +++ /dev/null @@ -1,24 +0,0 @@ -package dotty.tools -package dotc - -import dotty.tools.dotc.core.Types.Type -import dotty.tools.dotc.core.tasty.{TastyUnpickler, TastyBuffer, TastyPickler} -import util.SourceFile -import ast.{tpd, untpd} -import dotty.tools.dotc.core.Symbols._ - -class CompilationUnit(val source: SourceFile) { - - override def toString = source.toString - - var untpdTree: untpd.Tree = untpd.EmptyTree - - var tpdTree: tpd.Tree = tpd.EmptyTree - - def isJava = source.file.name.endsWith(".java") - - /** Pickled TASTY binaries, indexed by class. */ - var pickled: Map[ClassSymbol, Array[Byte]] = Map() - - var unpicklers: Map[ClassSymbol, TastyUnpickler] = Map() -} diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala deleted file mode 100644 index ad3249be2..000000000 --- a/src/dotty/tools/dotc/Compiler.scala +++ /dev/null @@ -1,145 +0,0 @@ -package dotty.tools -package dotc - -import core._ -import Contexts._ -import Periods._ -import Symbols._ -import Types._ -import Scopes._ -import typer.{FrontEnd, Typer, ImportInfo, RefChecks} -import reporting.{Reporter, ConsoleReporter} -import Phases.Phase -import transform._ -import util.FreshNameCreator -import transform.TreeTransforms.{TreeTransform, TreeTransformer} -import core.DenotTransformers.DenotTransformer -import core.Denotations.SingleDenotation - -import dotty.tools.backend.jvm.{LabelDefs, GenBCode, CollectSuperCalls} - -/** The central class of the dotc compiler. The job of a compiler is to create - * runs, which process given `phases` in a given `rootContext`. - */ -class Compiler { - - /** Meta-ordering constraint: - * - * DenotTransformers that change the signature of their denotation's info must go - * after erasure. The reason is that denotations are permanently referred to by - * TermRefs which contain a signature. If the signature of a symbol would change, - * all refs to it would become outdated - they could not be dereferenced in the - * new phase. - * - * After erasure, signature changing denot-transformers are OK because erasure - * will make sure that only term refs with fixed SymDenotations survive beyond it. This - * is possible because: - * - * - splitter has run, so every ident or select refers to a unique symbol - * - after erasure, asSeenFrom is the identity, so every reference has a - * plain SymDenotation, as opposed to a UniqueRefDenotation. - */ - def phases: List[List[Phase]] = - List( - List(new FrontEnd), // Compiler frontend: scanner, parser, namer, typer - List(new sbt.ExtractDependencies), // Sends information on classes' dependencies to sbt via callbacks - List(new PostTyper), // Additional checks and cleanups after type checking - List(new sbt.ExtractAPI), // Sends a representation of the API of classes to sbt via callbacks - List(new Pickler), // Generate TASTY info - List(new FirstTransform, // Some transformations to put trees into a canonical form - new CheckReentrant), // Internal use only: Check that compiled program has no data races involving global vars - List(new RefChecks, // Various checks mostly related to abstract members and overriding - new CheckStatic, // Check restrictions that apply to @static members - new ElimRepeated, // Rewrite vararg parameters and arguments - new NormalizeFlags, // Rewrite some definition flags - new ExtensionMethods, // Expand methods of value classes with extension methods - new ExpandSAMs, // Expand single abstract method closures to anonymous classes - new TailRec, // Rewrite tail recursion to loops - new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods - new ClassOf), // Expand `Predef.classOf` calls. - List(new TryCatchPatterns, // Compile cases in try/catch - new PatternMatcher, // Compile pattern matches - new ExplicitOuter, // Add accessors to outer classes from nested ones. - new ExplicitSelf, // Make references to non-trivial self types explicit as casts - new CrossCastAnd, // Normalize selections involving intersection types. - new Splitter), // Expand selections involving union types into conditionals - List(new VCInlineMethods, // Inlines calls to value class methods - new IsInstanceOfEvaluator, // Issues warnings when unreachable statements are present in match/if expressions - new SeqLiterals, // Express vararg arguments as arrays - new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods - new Getters, // Replace non-private vals and vars with getter defs (fields are added later) - new ElimByName, // Expand by-name parameters and arguments - new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings - new ResolveSuper, // Implement super accessors and add forwarders to trait methods - new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify. - List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. - List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types - new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations - new Mixin, // Expand trait fields and trait initializers - new LazyVals, // Expand lazy vals - new Memoize, // Add private fields to getters and setters - new LinkScala2ImplClasses, // Forward calls to the implementation classes of traits defined by Scala 2.11 - new NonLocalReturns, // Expand non-local returns - new CapturedVars, // Represent vars captured by closures as heap objects - new Constructors, // Collect initialization code in primary constructors - // Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it - new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions. - new GetClass), // Rewrites getClass calls on primitive types. - List(new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments - // Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here - new ElimStaticThis, // Replace `this` references to static objects by global identifiers - new Flatten, // Lift all inner classes to package scope - new RestoreScopes), // Repair scopes rendered invalid by moving definitions in prior phases of the group - List(new ExpandPrivate, // Widen private definitions accessed from nested classes - new SelectStatic, // get rid of selects that would be compiled into GetStatic - new CollectEntryPoints, // Find classes with main methods - new CollectSuperCalls, // Find classes that are called with super - new DropInlined, // Drop Inlined nodes, since backend has no use for them - new MoveStatics, // Move static methods to companion classes - new LabelDefs), // Converts calls to labels to jumps - List(new GenBCode) // Generate JVM bytecode - ) - - var runId = 1 - def nextRunId = { - runId += 1; runId - } - - /** Produces the following contexts, from outermost to innermost - * - * bootStrap: A context with next available runId and a scope consisting of - * the RootPackage _root_ - * start A context with RootClass as owner and the necessary initializations - * for type checking. - * imports For each element of RootImports, an import context - */ - def rootContext(implicit ctx: Context): Context = { - ctx.initialize()(ctx) - ctx.setPhasePlan(phases) - val rootScope = new MutableScope - val bootstrap = ctx.fresh - .setPeriod(Period(nextRunId, FirstPhaseId)) - .setScope(rootScope) - rootScope.enter(ctx.definitions.RootPackage)(bootstrap) - val start = bootstrap.fresh - .setOwner(defn.RootClass) - .setTyper(new Typer) - .setMode(Mode.ImplicitsEnabled) - .setTyperState(new MutableTyperState(ctx.typerState, ctx.typerState.reporter, isCommittable = true)) - .setFreshNames(new FreshNameCreator.Default) - ctx.initialize()(start) // re-initialize the base context with start - def addImport(ctx: Context, refFn: () => TermRef) = - ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx)) - (start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport) - } - - def reset()(implicit ctx: Context): Unit = { - ctx.base.reset() - ctx.runInfo.clear() - } - - def newRun(implicit ctx: Context): Run = { - reset() - new Run(this)(rootContext) - } -} diff --git a/src/dotty/tools/dotc/Driver.scala b/src/dotty/tools/dotc/Driver.scala deleted file mode 100644 index f54a23ad2..000000000 --- a/src/dotty/tools/dotc/Driver.scala +++ /dev/null @@ -1,134 +0,0 @@ -package dotty.tools.dotc - -import dotty.tools.FatalError -import config.CompilerCommand -import core.Contexts.{Context, ContextBase} -import util.DotClass -import reporting._ -import scala.util.control.NonFatal - -/** Run the Dotty compiler. - * - * Extending this class lets you customize many aspect of the compilation - * process, but in most cases you only need to call [[process]] on the - * existing object [[Main]]. - */ -abstract class Driver extends DotClass { - - protected def newCompiler(implicit ctx: Context): Compiler - - protected def emptyReporter: Reporter = new StoreReporter(null) - - protected def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = - if (fileNames.nonEmpty) - try { - val run = compiler.newRun - run.compile(fileNames) - run.printSummary() - } - catch { - case ex: FatalError => - ctx.error(ex.getMessage) // signals that we should fail compilation. - ctx.reporter - } - else ctx.reporter - - protected def initCtx = (new ContextBase).initialCtx - - protected def sourcesRequired = true - - def setup(args: Array[String], rootCtx: Context): (List[String], Context) = { - val ctx = rootCtx.fresh - val summary = CompilerCommand.distill(args)(ctx) - ctx.setSettings(summary.sstate) - val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ctx) - (fileNames, ctx) - } - - /** Entry point to the compiler that can be conveniently used with Java reflection. - * - * This entry point can easily be used without depending on the `dotty` package, - * you only need to depend on `dotty-interfaces` and call this method using - * reflection. This allows you to write code that will work against multiple - * versions of dotty without recompilation. - * - * The trade-off is that you can only pass a SimpleReporter to this method - * and not a normal Reporter which is more powerful. - * - * Usage example: [[https://github.com/lampepfl/dotty/tree/master/test/test/InterfaceEntryPointTest.scala]] - * - * @param args Arguments to pass to the compiler. - * @param simple Used to log errors, warnings, and info messages. - * The default reporter is used if this is `null`. - * @param callback Used to execute custom code during the compilation - * process. No callbacks will be executed if this is `null`. - * @return - */ - final def process(args: Array[String], simple: interfaces.SimpleReporter, - callback: interfaces.CompilerCallback): interfaces.ReporterResult = { - val reporter = if (simple == null) null else Reporter.fromSimpleReporter(simple) - process(args, reporter, callback) - } - - /** Principal entry point to the compiler. - * - * Usage example: [[https://github.com/lampepfl/dotty/tree/master/test/test/OtherEntryPointsTest.scala]] - * in method `runCompiler` - * - * @param args Arguments to pass to the compiler. - * @param reporter Used to log errors, warnings, and info messages. - * The default reporter is used if this is `null`. - * @param callback Used to execute custom code during the compilation - * process. No callbacks will be executed if this is `null`. - * @return The `Reporter` used. Use `Reporter#hasErrors` to check - * if compilation succeeded. - */ - final def process(args: Array[String], reporter: Reporter = null, - callback: interfaces.CompilerCallback = null): Reporter = { - val ctx = initCtx.fresh - if (reporter != null) - ctx.setReporter(reporter) - if (callback != null) - ctx.setCompilerCallback(callback) - process(args, ctx) - } - - /** Entry point to the compiler with no optional arguments. - * - * This overload is provided for compatibility reasons: the - * `RawCompiler` of sbt expects this method to exist and calls - * it using reflection. Keeping it means that we can change - * the other overloads without worrying about breaking compatibility - * with sbt. - */ - final def process(args: Array[String]): Reporter = - process(args, null: Reporter, null: interfaces.CompilerCallback) - - /** Entry point to the compiler using a custom `Context`. - * - * In most cases, you do not need a custom `Context` and should - * instead use one of the other overloads of `process`. However, - * the other overloads cannot be overriden, instead you - * should override this one which they call internally. - * - * Usage example: [[https://github.com/lampepfl/dotty/tree/master/test/test/OtherEntryPointsTest.scala]] - * in method `runCompilerWithContext` - * - * @param args Arguments to pass to the compiler. - * @param rootCtx The root Context to use. - * @return The `Reporter` used. Use `Reporter#hasErrors` to check - * if compilation succeeded. - */ - def process(args: Array[String], rootCtx: Context): Reporter = { - val (fileNames, ctx) = setup(args, rootCtx) - doCompile(newCompiler(ctx), fileNames)(ctx) - } - - def main(args: Array[String]): Unit = { - // Preload scala.util.control.NonFatal. Otherwise, when trying to catch a StackOverflowError, - // we may try to load it but fail with another StackOverflowError and lose the original exception, - // see <https://groups.google.com/forum/#!topic/scala-user/kte6nak-zPM>. - val _ = NonFatal - sys.exit(if (process(args).hasErrors) 1 else 0) - } -} diff --git a/src/dotty/tools/dotc/FromTasty.scala b/src/dotty/tools/dotc/FromTasty.scala deleted file mode 100644 index b060a2054..000000000 --- a/src/dotty/tools/dotc/FromTasty.scala +++ /dev/null @@ -1,107 +0,0 @@ -/* dotc - * Copyright 2005-2015 LAMP/EPFL - * @author Martin Odersky - */ -package dotty.tools -package dotc - -import core._ -import Contexts._ -import Symbols._ -import SymDenotations._ -import typer.FrontEnd -import Phases.Phase -import util._ -import reporting.Reporter -import Decorators._ -import dotty.tools.dotc.transform.Pickler -import tasty.DottyUnpickler -import ast.tpd._ - -/** Compiler for TASTY files. - * Usage: - * - * scala dotty.tools.dotc.FromTasty (option | classname)* - * - * Options are as for dotc. - * Classnames are fully qualified names of top-level classes that need to have a TASTY attribute. - * Example: - * - * scala dotty.tools.dotc.FromTasty -Xprint:front extMethods.T - */ -object FromTasty extends Driver { - override def newCompiler(implicit ctx: Context): Compiler = new TASTYCompiler - - class TASTYCompiler extends Compiler { - - override def phases: List[List[Phase]] = { - val backendPhases = super.phases.dropWhile { - case List(_: Pickler) => false - case _ => true - }.tail - List(new ReadTastyTreesFromClasses) :: backendPhases - } - - override def newRun(implicit ctx: Context): Run = { - reset() - new TASTYRun(this)(rootContext) - } - } - - class TASTYRun(comp: Compiler)(implicit ctx: Context) extends Run(comp) { - override def compile(classNames: List[String]) = { - units = classNames.map(new TASTYCompilationUnit(_)) - compileUnits() - } - } - - class TASTYCompilationUnit(val className: String) extends CompilationUnit(NoSource) { - override def toString = s"class file $className" - } - - object force extends TreeTraverser { - def traverse(tree: Tree)(implicit ctx: Context): Unit = traverseChildren(tree) - } - - class ReadTastyTreesFromClasses extends FrontEnd { - - override def isTyper = false - - override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = - units.map(readTASTY) - - def readTASTY(unit: CompilationUnit)(implicit ctx: Context): CompilationUnit = unit match { - case unit: TASTYCompilationUnit => - val className = unit.className.toTypeName - val clsd = - if (className.contains('.')) ctx.base.staticRef(className) - else defn.EmptyPackageClass.info.decl(className) - def cannotUnpickle(reason: String) = { - ctx.error(s"class $className cannot be unpickled because $reason") - unit - } - clsd match { - case clsd: ClassDenotation => - clsd.infoOrCompleter match { - case info: ClassfileLoader => - info.load(clsd) match { - case Some(unpickler: DottyUnpickler) => - val List(unpickled) = unpickler.body(ctx.addMode(Mode.ReadPositions)) - val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.sourceFile, Seq())) - unit1.tpdTree = unpickled - unit1.unpicklers += (clsd.classSymbol -> unpickler.unpickler) - force.traverse(unit1.tpdTree) - unit1 - case _ => - cannotUnpickle(s"its class file ${info.classfile} does not have a TASTY attribute") - } - case info => - cannotUnpickle(s"its info of type ${info.getClass} is not a ClassfileLoader") - } - case _ => - ctx.error(s"class not found: $className") - unit - } - } - } -} diff --git a/src/dotty/tools/dotc/Main.scala b/src/dotty/tools/dotc/Main.scala deleted file mode 100644 index a6844fbbc..000000000 --- a/src/dotty/tools/dotc/Main.scala +++ /dev/null @@ -1,9 +0,0 @@ -package dotty.tools -package dotc - -import core.Contexts.Context - -/** Main class of the `dotc` batch compiler. */ -object Main extends Driver { - override def newCompiler(implicit ctx: Context): Compiler = new Compiler -} diff --git a/src/dotty/tools/dotc/Resident.scala b/src/dotty/tools/dotc/Resident.scala deleted file mode 100644 index 56f6684d0..000000000 --- a/src/dotty/tools/dotc/Resident.scala +++ /dev/null @@ -1,58 +0,0 @@ -package dotty.tools -package dotc - -import core.Contexts.Context -import reporting.Reporter -import java.io.EOFException -import scala.annotation.tailrec - -/** A compiler which stays resident between runs. This is more of a PoC than - * something that's expected to be used often - * - * Usage: - * - * > scala dotty.tools.dotc.Resident <options> <initial files> - * - * dotc> "more options and files to compile" - * - * ... - * - * dotc> :reset // reset all options to the ones passed on the command line - * - * ... - * - * dotc> :q // quit - */ -class Resident extends Driver { - - object residentCompiler extends Compiler - - override def newCompiler(implicit ctx: Context): Compiler = ??? - - override def sourcesRequired = false - - private val quit = ":q" - private val reset = ":reset" - private val prompt = "dotc> " - - private def getLine() = { - Console.print(prompt) - try scala.io.StdIn.readLine() catch { case _: EOFException => quit } - } - - final override def process(args: Array[String], rootCtx: Context): Reporter = { - @tailrec def loop(args: Array[String], prevCtx: Context): Reporter = { - var (fileNames, ctx) = setup(args, prevCtx) - doCompile(residentCompiler, fileNames)(ctx) - var nextCtx = ctx - var line = getLine() - while (line == reset) { - nextCtx = rootCtx - line = getLine() - } - if (line.startsWith(quit)) ctx.reporter - else loop(line split "\\s+", nextCtx) - } - loop(args, rootCtx) - } -} diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala deleted file mode 100644 index 0f652ff0b..000000000 --- a/src/dotty/tools/dotc/Run.scala +++ /dev/null @@ -1,138 +0,0 @@ -package dotty.tools -package dotc - -import core._ -import Contexts._ -import Periods._ -import Symbols._ -import Phases._ -import Decorators._ -import dotty.tools.dotc.transform.TreeTransforms.TreeTransformer -import io.PlainFile -import scala.io.Codec -import util._ -import reporting.Reporter -import transform.TreeChecker -import rewrite.Rewrites -import java.io.{BufferedWriter, OutputStreamWriter} - -import scala.annotation.tailrec -import scala.reflect.io.VirtualFile -import scala.util.control.NonFatal - -/** A compiler run. Exports various methods to compile source files */ -class Run(comp: Compiler)(implicit ctx: Context) { - - assert(comp.phases.last.last.id <= Periods.MaxPossiblePhaseId) - assert(ctx.runId <= Periods.MaxPossibleRunId) - - var units: List[CompilationUnit] = _ - - def getSource(fileName: String): SourceFile = { - val f = new PlainFile(fileName) - if (f.isDirectory) { - ctx.error(s"expected file, received directory '$fileName'") - NoSource - } else if (f.exists) { - val encoding = ctx.settings.encoding.value - new SourceFile(f, Codec(encoding)) - } else { - ctx.error(s"not found: $fileName") - NoSource - } - } - - def compile(fileNames: List[String]): Unit = try { - val sources = fileNames map getSource - compileSources(sources) - } catch { - case NonFatal(ex) => - ctx.echo(i"exception occurred while compiling $units%, %") - throw ex - } - - /** TODO: There's a fundamental design problem here: We assemble phases using `squash` - * when we first build the compiler. But we modify them with -Yskip, -Ystop - * on each run. That modification needs to either transform the tree structure, - * or we need to assemble phases on each run, and take -Yskip, -Ystop into - * account. I think the latter would be preferable. - */ - def compileSources(sources: List[SourceFile]) = - if (sources forall (_.exists)) { - units = sources map (new CompilationUnit(_)) - compileUnits() - } - - protected def compileUnits() = Stats.monitorHeartBeat { - ctx.checkSingleThreaded() - val phases = ctx.squashPhases(ctx.phasePlan, - ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value) - ctx.usePhases(phases) - var lastPrintedTree: PrintedTree = NoPrintedTree - for (phase <- ctx.allPhases) - if (!ctx.reporter.hasErrors) { - val start = System.currentTimeMillis - units = phase.runOn(units) - if (ctx.settings.Xprint.value.containsPhase(phase)) { - for (unit <- units) { - lastPrintedTree = - printTree(lastPrintedTree)(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) - } - } - ctx.informTime(s"$phase ", start) - } - if (!ctx.reporter.hasErrors) Rewrites.writeBack() - for (unit <- units) - Stats.record("retained typed trees at end", unit.tpdTree.treeSize) - Stats.record("total trees at end", ast.Trees.ntrees) - } - - private sealed trait PrintedTree - private final case class SomePrintedTree(phase: String, tree: String) extends PrintedTree - private object NoPrintedTree extends PrintedTree - - private def printTree(last: PrintedTree)(implicit ctx: Context): PrintedTree = { - val unit = ctx.compilationUnit - val prevPhase = ctx.phase.prev // can be a mini-phase - val squashedPhase = ctx.squashed(prevPhase) - val treeString = unit.tpdTree.show - - ctx.echo(s"result of $unit after $squashedPhase:") - - last match { - case SomePrintedTree(phase, lastTreeSting) if lastTreeSting != treeString => - val msg = - if (!ctx.settings.XprintDiff.value && !ctx.settings.XprintDiffDel.value) treeString - else DiffUtil.mkColoredCodeDiff(treeString, lastTreeSting, ctx.settings.XprintDiffDel.value) - ctx.echo(msg) - SomePrintedTree(squashedPhase.toString, treeString) - - case SomePrintedTree(phase, lastTreeSting) => - ctx.echo(" Unchanged since " + phase) - last - - case NoPrintedTree => - ctx.echo(treeString) - SomePrintedTree(squashedPhase.toString, treeString) - } - } - - def compile(sourceCode: String): Unit = { - val virtualFile = new VirtualFile(sourceCode) // use source code as name as it's used for equals - val writer = new BufferedWriter(new OutputStreamWriter(virtualFile.output, "UTF-8")) // buffering is still advised by javadoc - writer.write(sourceCode) - writer.close() - compileSources(List(new SourceFile(virtualFile, Codec.UTF8))) - } - - /** The context created for this run */ - def runContext = ctx - - /** Print summary; return # of errors encountered */ - def printSummary(): Reporter = { - ctx.runInfo.printMaxConstraint() - val r = ctx.reporter - r.printSummary - r - } -} diff --git a/src/dotty/tools/dotc/ast/CheckTrees.scala.disabled b/src/dotty/tools/dotc/ast/CheckTrees.scala.disabled deleted file mode 100644 index 255619f35..000000000 --- a/src/dotty/tools/dotc/ast/CheckTrees.scala.disabled +++ /dev/null @@ -1,258 +0,0 @@ -package dotty.tools -package dotc -package ast - -import core._ -import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ -import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ - -// TODO: revise, integrate in a checking phase. -object CheckTrees { - - import tpd._ - - def check(p: Boolean, msg: => String = "")(implicit ctx: Context): Unit = assert(p, msg) - - def checkTypeArg(arg: Tree, bounds: TypeBounds)(implicit ctx: Context): Unit = { - check(arg.isValueType) - check(bounds contains arg.tpe) - } - - def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = { - var hoisted: Set[Symbol] = Set() - lazy val locals = ctx.typeAssigner.localSyms(block.stats).toSet - def isLocal(sym: Symbol): Boolean = - (locals contains sym) && !isHoistableClass(sym) - def isHoistableClass(sym: Symbol) = - sym.isClass && { - (hoisted contains sym) || { - hoisted += sym - !classLeaks(sym.asClass) - } - } - def leakingTypes(tp: Type): collection.Set[NamedType] = - tp namedPartsWith (tp => isLocal(tp.symbol)) - def typeLeaks(tp: Type): Boolean = leakingTypes(tp).nonEmpty - def classLeaks(sym: ClassSymbol): Boolean = - (ctx.owner is Method) || // can't hoist classes out of method bodies - (sym.info.parents exists typeLeaks) || - (sym.decls.toList exists (t => typeLeaks(t.info))) - leakingTypes(block.tpe) - } - - def checkType(tree: Tree)(implicit ctx: Context): Unit = tree match { - case Ident(name) => - case Select(qualifier, name) => - check(qualifier.isValue) - check(qualifier.tpe =:= tree.tpe.normalizedPrefix) - val denot = qualifier.tpe.member(name) - check(denot.exists) - check(denot.hasAltWith(_.symbol == tree.symbol)) - case This(cls) => - case Super(qual, mixin) => - check(qual.isValue) - val cls = qual.tpe.typeSymbol - check(cls.isClass) - case Apply(fn, args) => - def checkArg(arg: Tree, name: Name, formal: Type): Unit = { - arg match { - case NamedArg(argName, _) => - check(argName == name) - case _ => - check(arg.isValue) - } - check(arg.tpe <:< formal) - } - val MethodType(paramNames, paramTypes) = fn.tpe.widen // checked already at construction - (args, paramNames, paramTypes).zipped foreach checkArg - case TypeApply(fn, args) => - val pt @ PolyType(_) = fn.tpe.widen // checked already at construction - (args, pt.instantiateBounds(args map (_.tpe))).zipped foreach checkTypeArg - case Literal(const: Constant) => - case New(tpt) => - check(tpt.isValueType) - val cls = tpt.tpe.typeSymbol - check(cls.isClass) - check(!(cls is AbstractOrTrait)) - case Pair(left, right) => - check(left.isValue) - check(right.isValue) - case Typed(expr, tpt) => - check(tpt.isValueType) - expr.tpe.widen match { - case tp: MethodType => - val cls = tpt.tpe.typeSymbol - check(cls.isClass) - check((cls is Trait) || - cls.primaryConstructor.info.paramTypess.flatten.isEmpty) - val absMembers = tpt.tpe.abstractTermMembers - check(absMembers.size == 1) - check(tp <:< absMembers.head.info) - case _ => - check(expr.isValueOrPattern) - check(expr.tpe <:< tpt.tpe.translateParameterized(defn.RepeatedParamClass, defn.SeqClass)) - } - case NamedArg(name, arg) => - case Assign(lhs, rhs) => - check(lhs.isValue); check(rhs.isValue) - lhs.tpe match { - case ltpe: TermRef => - check(ltpe.symbol is Mutable) - case _ => - check(false) - } - check(rhs.tpe <:< lhs.tpe.widen) - case tree @ Block(stats, expr) => - check(expr.isValue) - check(escapingRefs(tree).isEmpty) - case If(cond, thenp, elsep) => - check(cond.isValue); check(thenp.isValue); check(elsep.isValue) - check(cond.tpe isRef defn.BooleanClass) - case Closure(env, meth, target) => - meth.tpe.widen match { - case mt @ MethodType(_, paramTypes) => - if (target.isEmpty) { - check(env.length < paramTypes.length) - for ((arg, formal) <- env zip paramTypes) - check(arg.tpe <:< formal) - } - else - // env is stored in class, not method - target.tpe match { - case SAMType(targetMeth) => - check(mt <:< targetMeth.info) - } - } - case Match(selector, cases) => - check(selector.isValue) - // are any checks that relate selector and patterns desirable? - case CaseDef(pat, guard, body) => - check(pat.isValueOrPattern); check(guard.isValue); check(body.isValue) - check(guard.tpe.derivesFrom(defn.BooleanClass)) - case Return(expr, from) => - check(expr.isValue); check(from.isTerm) - check(from.tpe.termSymbol.isRealMethod) - case Try(block, handler, finalizer) => - check(block.isTerm) - check(finalizer.isTerm) - check(handler.isTerm) - check(handler.tpe derivesFrom defn.FunctionClass(1)) - check(handler.tpe.baseArgInfos(defn.FunctionClass(1)).head <:< defn.ThrowableType) - case Throw(expr) => - check(expr.isValue) - check(expr.tpe.derivesFrom(defn.ThrowableClass)) - case SeqLiteral(elems) => - val elemtp = tree.tpe.elemType - for (elem <- elems) { - check(elem.isValue) - check(elem.tpe <:< elemtp) - } - case TypeTree(original) => - if (!original.isEmpty) { - check(original.isValueType) - check(original.tpe == tree.tpe) - } - case SingletonTypeTree(ref) => - check(ref.isValue) - check(ref.symbol.isStable) - case SelectFromTypeTree(qualifier, name) => - check(qualifier.isValueType) - check(qualifier.tpe =:= tree.tpe.normalizedPrefix) - val denot = qualifier.tpe.member(name) - check(denot.exists) - check(denot.symbol == tree.symbol) - case AndTypeTree(left, right) => - check(left.isValueType); check(right.isValueType) - case OrTypeTree(left, right) => - check(left.isValueType); check(right.isValueType) - case RefinedTypeTree(tpt, refinements) => - check(tpt.isValueType) - def checkRefinements(forbidden: Set[Symbol], rs: List[Tree]): Unit = rs match { - case r :: rs1 => - val rsym = r.symbol - check(rsym.isTerm || rsym.isAbstractOrAliasType) - if (rsym.isAbstractType) check(tpt.tpe.member(rsym.name).exists) - check(rsym.info forallParts { - case nt: NamedType => !(forbidden contains nt.symbol) - case _ => true - }) - checkRefinements(forbidden - rsym, rs1) - case nil => - } - checkRefinements(ctx.typeAssigner.localSyms(refinements).toSet, refinements) - case AppliedTypeTree(tpt, args) => - check(tpt.isValueType) - val tparams = tpt.tpe.typeParams - check(sameLength(tparams, args)) - (args, tparams map (_.info.bounds)).zipped foreach checkTypeArg - case TypeBoundsTree(lo, hi) => - check(lo.isValueType); check(hi.isValueType) - check(lo.tpe <:< hi.tpe) - case Bind(sym, body) => - check(body.isValueOrPattern) - check(!(tree.symbol is Method)) - body match { - case Ident(nme.WILDCARD) => - case _ => check(body.tpe.widen =:= tree.symbol.info) - } - case Alternative(alts) => - for (alt <- alts) check(alt.isValueOrPattern) - case UnApply(fun, implicits, args) => // todo: review - check(fun.isTerm) - for (arg <- args) check(arg.isValueOrPattern) - val funtpe @ MethodType(_, _) = fun.tpe.widen - fun.symbol.name match { // check arg arity - case nme.unapplySeq => - // args need to be wrapped in (...: _*) - check(args.length == 1) - check(args.head.isInstanceOf[SeqLiteral]) - case nme.unapply => - val rtp = funtpe.resultType - if (rtp isRef defn.BooleanClass) - check(args.isEmpty) - else { - check(rtp isRef defn.OptionClass) - val normArgs = rtp.argTypesHi match { - case optionArg :: Nil => - optionArg.argTypesHi match { - case Nil => - optionArg :: Nil - case tupleArgs if defn.isTupleType(optionArg) => - tupleArgs - } - case _ => - check(false) - Nil - } - check(sameLength(normArgs, args)) - } - } - case ValDef(mods, name, tpt, rhs) => - check(!(tree.symbol is Method)) - if (!rhs.isEmpty) { - check(rhs.isValue) - check(rhs.tpe <:< tpt.tpe) - } - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - check(tree.symbol is Method) - if (!rhs.isEmpty) { - check(rhs.isValue) - check(rhs.tpe <:< tpt.tpe) - } - case TypeDef(mods, name, tpt) => - check(tpt.isInstanceOf[Template] || tpt.tpe.isInstanceOf[TypeBounds]) - case Template(constr, parents, selfType, body) => - case Import(expr, selectors) => - check(expr.isValue) - check(expr.tpe.termSymbol.isStable) - case PackageDef(pid, stats) => - check(pid.isTerm) - check(pid.symbol is Package) - case Annotated(annot, arg) => - check(annot.isInstantiation) - check(annot.symbol.owner.isSubClass(defn.AnnotationClass)) - check(arg.isValueType || arg.isValue) - case EmptyTree => - } -} - diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala deleted file mode 100644 index 366a0e225..000000000 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ /dev/null @@ -1,1089 +0,0 @@ -package dotty.tools -package dotc -package ast - -import core._ -import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ -import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ -import Decorators._ -import language.higherKinds -import collection.mutable.ListBuffer -import util.Property -import reporting.diagnostic.messages._ - -object desugar { - import untpd._ - - /** Tags a .withFilter call generated by desugaring a for expression. - * Such calls can alternatively be rewritten to use filter. - */ - val MaybeFilter = new Property.Key[Unit] - - /** Info of a variable in a pattern: The named tree and its type */ - private type VarInfo = (NameTree, Tree) - - /** Names of methods that are added unconditionally to case classes */ - def isDesugaredCaseClassMethodName(name: Name)(implicit ctx: Context): Boolean = - name == nme.isDefined || - name == nme.copy || - name == nme.productArity || - name.isSelectorName - -// ----- DerivedTypeTrees ----------------------------------- - - class SetterParamTree extends DerivedTypeTree { - def derivedType(sym: Symbol)(implicit ctx: Context) = sym.info.resultType - } - - class TypeRefTree extends DerivedTypeTree { - def derivedType(sym: Symbol)(implicit ctx: Context) = sym.typeRef - } - - class DerivedFromParamTree extends DerivedTypeTree { - - /** Make sure that for all enclosing module classes their companion lasses - * are completed. Reason: We need the constructor of such companion classes to - * be completed so that OriginalSymbol attachments are pushed to DerivedTypeTrees - * in apply/unapply methods. - */ - override def ensureCompletions(implicit ctx: Context) = - if (!(ctx.owner is Package)) - if (ctx.owner.isClass) { - ctx.owner.ensureCompleted() - if (ctx.owner is ModuleClass) - ctx.owner.linkedClass.ensureCompleted() - } - else ensureCompletions(ctx.outer) - - /** Return info of original symbol, where all references to siblings of the - * original symbol (i.e. sibling and original symbol have the same owner) - * are rewired to same-named parameters or accessors in the scope enclosing - * the current scope. The current scope is the scope owned by the defined symbol - * itself, that's why we have to look one scope further out. If the resulting - * type is an alias type, dealias it. This is necessary because the - * accessor of a type parameter is a private type alias that cannot be accessed - * from subclasses. - */ - def derivedType(sym: Symbol)(implicit ctx: Context) = { - val relocate = new TypeMap { - val originalOwner = sym.owner - def apply(tp: Type) = tp match { - case tp: NamedType if tp.symbol.exists && (tp.symbol.owner eq originalOwner) => - val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next - var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol - if (local.exists) (defctx.owner.thisType select local).dealias - else throw new java.lang.Error( - s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}" - ) - case _ => - mapOver(tp) - } - } - relocate(sym.info) - } - } - - /** A type definition copied from `tdef` with a rhs typetree derived from it */ - def derivedTypeParam(tdef: TypeDef) = - cpy.TypeDef(tdef)( - rhs = new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef) - - /** A value definition copied from `vdef` with a tpt typetree derived from it */ - def derivedTermParam(vdef: ValDef) = - cpy.ValDef(vdef)( - tpt = new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef) - -// ----- Desugar methods ------------------------------------------------- - - /** var x: Int = expr - * ==> - * def x: Int = expr - * def x_=($1: <TypeTree()>): Unit = () - */ - def valDef(vdef: ValDef)(implicit ctx: Context): Tree = { - val ValDef(name, tpt, rhs) = vdef - val mods = vdef.mods - def setterNeeded = - (mods is Mutable) && ctx.owner.isClass && (!(mods is PrivateLocal) || (ctx.owner is Trait)) - if (setterNeeded) { - // todo: copy of vdef as getter needed? - // val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ? - // right now vdef maps via expandedTree to a thicket which concerns itself. - // I don't see a problem with that but if there is one we can avoid it by making a copy here. - val setterParam = makeSyntheticParameter(tpt = (new SetterParamTree).watching(vdef)) - val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral - val setter = cpy.DefDef(vdef)( - name = name.setterName, - tparams = Nil, - vparamss = (setterParam :: Nil) :: Nil, - tpt = TypeTree(defn.UnitType), - rhs = setterRhs - ).withMods((mods | Accessor) &~ CaseAccessor) // rhs gets filled in later, when field is generated and getter has parameters - Thicket(vdef, setter) - } - else vdef - } - - /** Expand context bounds to evidence params. E.g., - * - * def f[T >: L <: H : B](params) - * ==> - * def f[T >: L <: H](params)(implicit evidence$0: B[T]) - * - * Expand default arguments to default getters. E.g, - * - * def f[T: B](x: Int = 1)(y: String = x + "m") = ... - * ==> - * def f[T](x: Int)(y: String)(implicit evidence$0: B[T]) = ... - * def f$default$1[T] = 1 - * def f$default$2[T](x: Int) = x + "m" - */ - def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = { - val DefDef(name, tparams, vparamss, tpt, rhs) = meth - val mods = meth.mods - val epbuf = new ListBuffer[ValDef] - val tparams1 = tparams mapConserve { - case tparam @ TypeDef(_, ContextBounds(tbounds, cxbounds)) => - for (cxbound <- cxbounds) { - val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param - val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName - epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit) - } - cpy.TypeDef(tparam)(rhs = tbounds) - case tparam => - tparam - } - - val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList) - - /** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */ - def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match { - case vparams :: vparamss1 => - val len = vparams.length - if (n >= len) vparams :: takeUpTo(vparamss1, n - len) else Nil - case _ => - Nil - } - - def normalizedVparamss = meth1.vparamss map (_ map (vparam => - cpy.ValDef(vparam)(rhs = EmptyTree))) - - def dropContextBound(tparam: TypeDef) = tparam.rhs match { - case ContextBounds(tbounds, _) => cpy.TypeDef(tparam)(rhs = tbounds) - case _ => tparam - } - - def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match { - case (vparam :: vparams) :: vparamss1 => - def defaultGetter: DefDef = - DefDef( - name = meth.name.defaultGetterName(n), - tparams = meth.tparams.map(tparam => dropContextBound(toDefParam(tparam))), - vparamss = takeUpTo(normalizedVparamss, n), - tpt = TypeTree(), - rhs = vparam.rhs - ).withMods(Modifiers(mods.flags & AccessFlags, mods.privateWithin)) - val rest = defaultGetters(vparams :: vparamss1, n + 1) - if (vparam.rhs.isEmpty) rest else defaultGetter :: rest - case Nil :: vparamss1 => - defaultGetters(vparamss1, n) - case nil => - Nil - } - - val defGetters = defaultGetters(vparamss, 0) - if (defGetters.isEmpty) meth1 - else { - val meth2 = cpy.DefDef(meth1)(vparamss = normalizedVparamss) - .withMods(meth1.mods | DefaultParameterized) - Thicket(meth2 :: defGetters) - } - } - - // Add all evidence parameters in `params` as implicit parameters to `meth` */ - private def addEvidenceParams(meth: DefDef, params: List[ValDef])(implicit ctx: Context): DefDef = - params match { - case Nil => - meth - case evidenceParams => - val vparamss1 = meth.vparamss.reverse match { - case (vparams @ (vparam :: _)) :: rvparamss if vparam.mods is Implicit => - ((vparams ++ evidenceParams) :: rvparamss).reverse - case _ => - meth.vparamss :+ evidenceParams - } - cpy.DefDef(meth)(vparamss = vparamss1) - } - - /** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */ - private def evidenceParams(meth: DefDef)(implicit ctx: Context): List[ValDef] = - meth.vparamss.reverse match { - case (vparams @ (vparam :: _)) :: _ if vparam.mods is Implicit => - vparams.dropWhile(!_.name.startsWith(nme.EVIDENCE_PARAM_PREFIX)) - case _ => - Nil - } - - /** Fill in empty type bounds with Nothing/Any. Expand private local type parameters as follows: - * - * class C[v T] - * ==> - * class C { type v C$T; type v T = C$T } - */ - def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = { - if (tdef.mods is PrivateLocalParam) { - val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner)) - .withMods(tdef.mods &~ PrivateLocal | ExpandedName) - val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam)) - .withMods(tdef.mods & VarianceFlags | PrivateLocalParamAccessor | Synthetic) - Thicket(tparam, alias) - } - else tdef - } - - @sharable private val synthetic = Modifiers(Synthetic) - - private def toDefParam(tparam: TypeDef): TypeDef = - tparam.withMods(tparam.rawMods & EmptyFlags | Param) - private def toDefParam(vparam: ValDef): ValDef = - vparam.withMods(vparam.rawMods & Implicit | Param) - - /** The expansion of a class definition. See inline comments for what is involved */ - def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = { - val className = checkNotReservedName(cdef).asTypeName - val impl @ Template(constr0, parents, self, _) = cdef.rhs - val mods = cdef.mods - val companionMods = mods.withFlags((mods.flags & AccessFlags).toCommonFlags) - - val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match { - case meth: DefDef => (meth, Nil) - case Thicket((meth: DefDef) :: defaults) => (meth, defaults) - } - - // The original type and value parameters in the constructor already have the flags - // needed to be type members (i.e. param, and possibly also private and local unless - // prefixed by type or val). `tparams` and `vparamss` are the type parameters that - // go in `constr`, the constructor after desugaring. - - /** Does `tree' look like a reference to AnyVal? Temporary test before we have inline classes */ - def isAnyVal(tree: Tree): Boolean = tree match { - case Ident(tpnme.AnyVal) => true - case Select(qual, tpnme.AnyVal) => isScala(qual) - case _ => false - } - def isScala(tree: Tree): Boolean = tree match { - case Ident(nme.scala_) => true - case Select(Ident(nme.ROOTPKG), nme.scala_) => true - case _ => false - } - - val isCaseClass = mods.is(Case) && !mods.is(Module) - val isValueClass = parents.nonEmpty && isAnyVal(parents.head) - // This is not watertight, but `extends AnyVal` will be replaced by `inline` later. - - val constrTparams = constr1.tparams map toDefParam - val constrVparamss = - if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty - if (isCaseClass) - ctx.error(CaseClassMissingParamList(cdef), cdef.namePos) - ListOfNil - } - else constr1.vparamss.nestedMap(toDefParam) - val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss) - - // Add constructor type parameters and evidence implicit parameters - // to auxiliary constructors - val normalizedBody = impl.body map { - case ddef: DefDef if ddef.name.isConstructorName => - addEvidenceParams( - cpy.DefDef(ddef)(tparams = constrTparams), - evidenceParams(constr1).map(toDefParam)) - case stat => - stat - } - - val derivedTparams = constrTparams map derivedTypeParam - val derivedVparamss = constrVparamss nestedMap derivedTermParam - val arity = constrVparamss.head.length - - var classTycon: Tree = EmptyTree - - // a reference to the class type, with all parameters given. - val classTypeRef/*: Tree*/ = { - // -language:keepUnions difference: classTypeRef needs type annotation, otherwise - // infers Ident | AppliedTypeTree, which - // renders the :\ in companions below untypable. - classTycon = (new TypeRefTree) withPos cdef.pos.startPos // watching is set at end of method - val tparams = impl.constr.tparams - if (tparams.isEmpty) classTycon else AppliedTypeTree(classTycon, tparams map refOfDef) - } - - // new C[Ts](paramss) - lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef) - - // Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams) - // def isDefined = true - // def productArity = N - // def _1 = this.p1 - // ... - // def _N = this.pN - // def copy(p1: T1 = p1: @uncheckedVariance, ..., - // pN: TN = pN: @uncheckedVariance)(moreParams) = - // new C[...](p1, ..., pN)(moreParams) - // - // Note: copy default parameters need @uncheckedVariance; see - // neg/t1843-variances.scala for a test case. The test would give - // two errors without @uncheckedVariance, one of them spurious. - val caseClassMeths = - if (isCaseClass) { - def syntheticProperty(name: TermName, rhs: Tree) = - DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic) - val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true))) - val caseParams = constrVparamss.head.toArray - val productElemMeths = for (i <- 0 until arity) yield - syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name)) - def isRepeated(tree: Tree): Boolean = tree match { - case PostfixOp(_, nme.raw.STAR) => true - case ByNameTypeTree(tree1) => isRepeated(tree1) - case _ => false - } - val hasRepeatedParam = constrVparamss.exists(_.exists { - case ValDef(_, tpt, _) => isRepeated(tpt) - case _ => false - }) - - val copyMeths = - if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued - else { - def copyDefault(vparam: ValDef) = - makeAnnotated(defn.UncheckedVarianceAnnot, refOfDef(vparam)) - val copyFirstParams = derivedVparamss.head.map(vparam => - cpy.ValDef(vparam)(rhs = copyDefault(vparam))) - val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => - cpy.ValDef(vparam)(rhs = EmptyTree)) - DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) - .withMods(synthetic) :: Nil - } - copyMeths ::: isDefinedMeth :: productElemMeths.toList - } - else Nil - - def anyRef = ref(defn.AnyRefAlias.typeRef) - def productConstr(n: Int) = { - val tycon = scalaDot((tpnme.Product.toString + n).toTypeName) - val targs = constrVparamss.head map (_.tpt) - if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs) - } - - // Case classes and case objects get a ProductN parent - var parents1 = parents - if (mods.is(Case) && arity <= Definitions.MaxTupleArity) - parents1 = parents1 :+ productConstr(arity) - - // The thicket which is the desugared version of the companion object - // synthetic object C extends parentTpt { defs } - def companionDefs(parentTpt: Tree, defs: List[Tree]) = - moduleDef( - ModuleDef( - className.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs)) - .withMods(companionMods | Synthetic)) - .withPos(cdef.pos).toList - - // The companion object definitions, if a companion is needed, Nil otherwise. - // companion definitions include: - // 1. If class is a case class case class C[Ts](p1: T1, ..., pN: TN)(moreParams): - // def apply[Ts](p1: T1, ..., pN: TN)(moreParams) = new C[Ts](p1, ..., pN)(moreParams) (unless C is abstract) - // def unapply[Ts]($1: C[Ts]) = $1 - // 2. The default getters of the constructor - // The parent of the companion object of a non-parameterized case class - // (T11, ..., T1N) => ... => (TM1, ..., TMN) => C - // For all other classes, the parent is AnyRef. - val companions = - if (isCaseClass) { - val parent = - if (constrTparams.nonEmpty || - constrVparamss.length > 1 || - mods.is(Abstract) || - constr.mods.is(Private)) anyRef - // todo: also use anyRef if constructor has a dependent method type (or rule that out)! - else (constrVparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe)) - val applyMeths = - if (mods is Abstract) Nil - else - DefDef(nme.apply, derivedTparams, derivedVparamss, TypeTree(), creatorExpr) - .withFlags(Synthetic | (constr1.mods.flags & DefaultParameterized)) :: Nil - val unapplyMeth = { - val unapplyParam = makeSyntheticParameter(tpt = classTypeRef) - val unapplyRHS = if (arity == 0) Literal(Constant(true)) else Ident(unapplyParam.name) - DefDef(nme.unapply, derivedTparams, (unapplyParam :: Nil) :: Nil, TypeTree(), unapplyRHS) - .withMods(synthetic) - } - companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters) - } - else if (defaultGetters.nonEmpty) - companionDefs(anyRef, defaultGetters) - else if (isValueClass) - companionDefs(anyRef, Nil) - else Nil - - - // For an implicit class C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, .., pMN: TMN), the method - // synthetic implicit C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, ..., pMN: TMN): C[Ts] = - // new C[Ts](p11, ..., p1N) ... (pM1, ..., pMN) = - val implicitWrappers = - if (!mods.is(Implicit)) - Nil - else if (ctx.owner is Package) { - ctx.error(TopLevelImplicitClass(cdef), cdef.pos) - Nil - } - else if (isCaseClass) { - ctx.error(ImplicitCaseClass(cdef), cdef.pos) - Nil - } - else - // implicit wrapper is typechecked in same scope as constructor, so - // we can reuse the constructor parameters; no derived params are needed. - DefDef(className.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr) - .withMods(companionMods | Synthetic | Implicit) - .withPos(cdef.pos) :: Nil - - val self1 = { - val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt - if (self.isEmpty) self - else cpy.ValDef(self)(tpt = selfType).withMods(self.mods | SelfName) - } - - val cdef1 = { - val originalTparams = constr1.tparams.toIterator - val originalVparams = constr1.vparamss.toIterator.flatten - val tparamAccessors = derivedTparams.map(_.withMods(originalTparams.next.mods)) - val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags - val vparamAccessors = derivedVparamss.flatten.map(_.withMods(originalVparams.next.mods | caseAccessor)) - cpy.TypeDef(cdef)( - name = className, - rhs = cpy.Template(impl)(constr, parents1, self1, - tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)) - } - - // install the watch on classTycon - classTycon match { - case tycon: DerivedTypeTree => tycon.watching(cdef1) - case _ => - } - - flatTree(cdef1 :: companions ::: implicitWrappers) - } - - val AccessOrSynthetic = AccessFlags | Synthetic - - /** Expand - * - * object name extends parents { self => body } - * - * to: - * <module> val name: name$ = New(name$) - * <module> final class name$ extends parents { self: name.type => body } - */ - def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = { - val moduleName = checkNotReservedName(mdef).asTermName - val tmpl = mdef.impl - val mods = mdef.mods - if (mods is Package) - PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, tmpl).withMods(mods &~ Package) :: Nil) - else { - val clsName = moduleName.moduleClassName - val clsRef = Ident(clsName) - val modul = ValDef(moduleName, clsRef, New(clsRef, Nil)) - .withMods(mods | ModuleCreationFlags | mods.flags & AccessFlags) - .withPos(mdef.pos) - val ValDef(selfName, selfTpt, _) = tmpl.self - val selfMods = tmpl.self.mods - if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), tmpl.self.pos) - val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(moduleName)), tmpl.self.rhs) - .withMods(selfMods) - .withPos(tmpl.self.pos orElse tmpl.pos.startPos) - val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body) - val cls = TypeDef(clsName, clsTmpl) - .withMods(mods.toTypeFlags & RetainedModuleClassFlags | ModuleClassCreationFlags) - Thicket(modul, classDef(cls).withPos(mdef.pos)) - } - } - - /** The name of `mdef`, after checking that it does not redefine a Scala core class. - * If it does redefine, issue an error and return a mangled name instead of the original one. - */ - def checkNotReservedName(mdef: MemberDef)(implicit ctx: Context): Name = { - val name = mdef.name - if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) { - def kind = if (name.isTypeName) "class" else "object" - ctx.error(em"illegal redefinition of standard $kind $name", mdef.pos) - name.errorName - } - else name - } - - /** val p1, ..., pN: T = E - * ==> - * makePatDef[[val p1: T1 = E]]; ...; makePatDef[[val pN: TN = E]] - */ - def patDef(pdef: PatDef)(implicit ctx: Context): Tree = { - val PatDef(mods, pats, tpt, rhs) = pdef - val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt)) - flatTree(pats1 map (makePatDef(pdef, mods, _, rhs))) - } - - /** If `pat` is a variable pattern, - * - * val/var/lazy val p = e - * - * Otherwise, in case there is exactly one variable x_1 in pattern - * val/var/lazy val p = e ==> val/var/lazy val x_1 = (e: @unchecked) match (case p => (x_1)) - * - * in case there are zero or more than one variables in pattern - * val/var/lazy p = e ==> private synthetic [lazy] val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N)) - * val/var/def x_1 = t$._1 - * ... - * val/var/def x_N = t$._N - * If the original pattern variable carries a type annotation, so does the corresponding - * ValDef or DefDef. - */ - def makePatDef(original: Tree, mods: Modifiers, pat: Tree, rhs: Tree)(implicit ctx: Context): Tree = pat match { - case VarPattern(named, tpt) => - derivedValDef(original, named, tpt, rhs, mods) - case _ => - val rhsUnchecked = makeAnnotated(defn.UncheckedAnnot, rhs) - val vars = getVariables(pat) - val isMatchingTuple: Tree => Boolean = { - case Tuple(es) => es.length == vars.length - case _ => false - } - val ids = for ((named, _) <- vars) yield Ident(named.name) - val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids)) - val matchExpr = - if (forallResults(rhs, isMatchingTuple)) rhs - else Match(rhsUnchecked, caseDef :: Nil) - vars match { - case Nil => - matchExpr - case (named, tpt) :: Nil => - derivedValDef(original, named, tpt, matchExpr, mods) - case _ => - val tmpName = ctx.freshName().toTermName - val patMods = mods & (AccessFlags | Lazy) | Synthetic - val firstDef = - ValDef(tmpName, TypeTree(), matchExpr) - .withPos(pat.pos.union(rhs.pos)).withMods(patMods) - def selector(n: Int) = Select(Ident(tmpName), nme.selectorName(n)) - val restDefs = - for (((named, tpt), n) <- vars.zipWithIndex) - yield - if (mods is Lazy) derivedDefDef(original, named, tpt, selector(n), mods &~ Lazy) - else derivedValDef(original, named, tpt, selector(n), mods) - flatTree(firstDef :: restDefs) - } - } - - /** Expand variable identifier x to x @ _ */ - def patternVar(tree: Tree)(implicit ctx: Context) = { - val Ident(name) = tree - Bind(name, Ident(nme.WILDCARD)).withPos(tree.pos) - } - - def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match { - case tree: ValDef => valDef(tree) - case tree: TypeDef => if (tree.isClassDef) classDef(tree) else typeDef(tree) - case tree: DefDef => defDef(tree) - case tree: ModuleDef => moduleDef(tree) - case tree: PatDef => patDef(tree) - } - - /** { stats; <empty > } - * ==> - * { stats; () } - */ - def block(tree: Block)(implicit ctx: Context): Block = tree.expr match { - case EmptyTree => - cpy.Block(tree)(tree.stats, - unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos)) - case _ => - tree - } - - /** EmptyTree in lower bound ==> Nothing - * EmptyTree in upper bounds ==> Any - */ - def typeBoundsTree(tree: TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = { - val TypeBoundsTree(lo, hi) = tree - val lo1 = if (lo.isEmpty) untpd.TypeTree(defn.NothingType) else lo - val hi1 = if (hi.isEmpty) untpd.TypeTree(defn.AnyType) else hi - cpy.TypeBoundsTree(tree)(lo1, hi1) - } - - /** Make closure corresponding to function. - * params => body - * ==> - * def $anonfun(params) = body - * Closure($anonfun) - * - * If `inlineable` is true, tag $anonfun with an @inline annotation. - */ - def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), inlineable: Boolean)(implicit ctx: Context) = { - var mods = synthetic - if (inlineable) mods |= Inline - Block( - DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(mods), - Closure(Nil, Ident(nme.ANON_FUN), EmptyTree)) - } - - /** If `nparams` == 1, expand partial function - * - * { cases } - * ==> - * x$1 => (x$1 @unchecked) match { cases } - * - * If `nparams` != 1, expand instead to - * - * (x$1, ..., x$n) => (x$0, ..., x${n-1} @unchecked) match { cases } - */ - def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1, unchecked: Boolean = true)(implicit ctx: Context) = { - val params = (1 to nparams).toList.map(makeSyntheticParameter(_)) - val selector = makeTuple(params.map(p => Ident(p.name))) - - if (unchecked) - Function(params, Match(Annotated(selector, New(ref(defn.UncheckedAnnotType))), cases)) - else - Function(params, Match(selector, cases)) - } - - /** Map n-ary function `(p1, ..., pn) => body` where n != 1 to unary function as follows: - * - * x$1 => { - * def p1 = x$1._1 - * ... - * def pn = x$1._n - * body - * } - */ - def makeTupledFunction(params: List[ValDef], body: Tree)(implicit ctx: Context): Tree = { - val param = makeSyntheticParameter() - def selector(n: Int) = Select(refOfDef(param), nme.selectorName(n)) - val vdefs = - params.zipWithIndex.map{ - case (param, idx) => - DefDef(param.name, Nil, Nil, TypeTree(), selector(idx)).withPos(param.pos) - } - Function(param :: Nil, Block(vdefs, body)) - } - - /** Add annotation with class `cls` to tree: - * tree @cls - */ - def makeAnnotated(cls: Symbol, tree: Tree)(implicit ctx: Context) = - Annotated(tree, untpd.New(untpd.TypeTree(cls.typeRef), Nil)) - - private def derivedValDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(implicit ctx: Context) = { - val vdef = ValDef(named.name.asTermName, tpt, rhs) - .withMods(mods) - .withPos(original.pos.withPoint(named.pos.start)) - val mayNeedSetter = valDef(vdef) - mayNeedSetter - } - - private def derivedDefDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers) = - DefDef(named.name.asTermName, Nil, Nil, tpt, rhs) - .withMods(mods) - .withPos(original.pos.withPoint(named.pos.start)) - - /** Main desugaring method */ - def apply(tree: Tree)(implicit ctx: Context): Tree = { - - /** { label def lname(): Unit = rhs; call } - */ - def labelDefAndCall(lname: TermName, rhs: Tree, call: Tree) = { - val ldef = DefDef(lname, Nil, ListOfNil, TypeTree(defn.UnitType), rhs).withFlags(Label) - Block(ldef, call) - } - - /** Translate infix operation expression left op right - */ - def makeBinop(left: Tree, op: Name, right: Tree): Tree = { - def assignToNamedArg(arg: Tree) = arg match { - case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs) - case _ => arg - } - if (isLeftAssoc(op)) { - val args: List[Tree] = right match { - case Parens(arg) => assignToNamedArg(arg) :: Nil - case Tuple(args) => args mapConserve assignToNamedArg - case _ => right :: Nil - } - Apply(Select(left, op), args) - } else { - val x = ctx.freshName().toTermName - new InfixOpBlock( - ValDef(x, TypeTree(), left).withMods(synthetic), - Apply(Select(right, op), Ident(x))) - } - } - - /** Create tree for for-comprehension `<for (enums) do body>` or - * `<for (enums) yield body>` where mapName and flatMapName are chosen - * corresponding to whether this is a for-do or a for-yield. - * The creation performs the following rewrite rules: - * - * 1. - * - * for (P <- G) E ==> G.foreach (P => E) - * - * Here and in the following (P => E) is interpreted as the function (P => E) - * if P is a variable pattern and as the partial function { case P => E } otherwise. - * - * 2. - * - * for (P <- G) yield E ==> G.map (P => E) - * - * 3. - * - * for (P_1 <- G_1; P_2 <- G_2; ...) ... - * ==> - * G_1.flatMap (P_1 => for (P_2 <- G_2; ...) ...) - * - * 4. - * - * for (P <- G; E; ...) ... - * => - * for (P <- G.filter (P => E); ...) ... - * - * 5. For any N: - * - * for (P_1 <- G; P_2 = E_2; val P_N = E_N; ...) - * ==> - * for (TupleN(P_1, P_2, ... P_N) <- - * for (x_1 @ P_1 <- G) yield { - * val x_2 @ P_2 = E_2 - * ... - * val x_N & P_N = E_N - * TupleN(x_1, ..., x_N) - * } ...) - * - * If any of the P_i are variable patterns, the corresponding `x_i @ P_i` is not generated - * and the variable constituting P_i is used instead of x_i - * - * @param mapName The name to be used for maps (either map or foreach) - * @param flatMapName The name to be used for flatMaps (either flatMap or foreach) - * @param enums The enumerators in the for expression - * @param body The body of the for expression - */ - def makeFor(mapName: TermName, flatMapName: TermName, enums: List[Tree], body: Tree): Tree = ctx.traceIndented(i"make for ${ForYield(enums, body)}", show = true) { - - /** Make a function value pat => body. - * If pat is a var pattern id: T then this gives (id: T) => body - * Otherwise this gives { case pat => body } - */ - def makeLambda(pat: Tree, body: Tree): Tree = pat match { - case VarPattern(named, tpt) => - Function(derivedValDef(pat, named, tpt, EmptyTree, Modifiers(Param)) :: Nil, body) - case _ => - makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil, unchecked = false) - } - - /** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap - * it in a Bind with a fresh name. Return the transformed pattern, and the identifier - * that refers to the bound variable for the pattern. - */ - def makeIdPat(pat: Tree): (Tree, Ident) = pat match { - case Bind(name, _) => (pat, Ident(name)) - case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id) - case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id) - case _ => - val name = ctx.freshName().toTermName - (Bind(name, pat), Ident(name)) - } - - /** Add MaybeFilter attachment */ - def orFilter(tree: Tree): tree.type = { - tree.putAttachment(MaybeFilter, ()) - tree - } - - /** Make a pattern filter: - * rhs.withFilter { case pat => true case _ => false } - * - * On handling irrefutable patterns: - * The idea is to wait until the pattern matcher sees a call - * - * xs withFilter { cases } - * - * where cases can be proven to be refutable i.e. cases would be - * equivalent to { case _ => true } - * - * In that case, compile to - * - * xs withFilter alwaysTrue - * - * where `alwaysTrue` is a predefined function value: - * - * val alwaysTrue: Any => Boolean = true - * - * In the libraries operations can take advantage of alwaysTrue to shortcircuit the - * withFilter call. - * - * def withFilter(f: Elem => Boolean) = - * if (f eq alwaysTrue) this // or rather identity filter monadic applied to this - * else real withFilter - */ - def makePatFilter(rhs: Tree, pat: Tree): Tree = { - val cases = List( - CaseDef(pat, EmptyTree, Literal(Constant(true))), - CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))) - Apply(orFilter(Select(rhs, nme.withFilter)), makeCaseLambda(cases)) - } - - /** Is pattern `pat` irrefutable when matched against `rhs`? - * We only can do a simple syntactic check here; a more refined check - * is done later in the pattern matcher (see discussion in @makePatFilter). - */ - def isIrrefutable(pat: Tree, rhs: Tree): Boolean = { - def matchesTuple(pats: List[Tree], rhs: Tree): Boolean = rhs match { - case Tuple(trees) => (pats corresponds trees)(isIrrefutable) - case Parens(rhs1) => matchesTuple(pats, rhs1) - case Block(_, rhs1) => matchesTuple(pats, rhs1) - case If(_, thenp, elsep) => matchesTuple(pats, thenp) && matchesTuple(pats, elsep) - case Match(_, cases) => cases forall (matchesTuple(pats, _)) - case CaseDef(_, _, rhs1) => matchesTuple(pats, rhs1) - case Throw(_) => true - case _ => false - } - pat match { - case Bind(_, pat1) => isIrrefutable(pat1, rhs) - case Parens(pat1) => isIrrefutable(pat1, rhs) - case Tuple(pats) => matchesTuple(pats, rhs) - case _ => isVarPattern(pat) - } - } - - def isIrrefutableGenFrom(gen: GenFrom): Boolean = - gen.isInstanceOf[IrrefutableGenFrom] || isIrrefutable(gen.pat, gen.expr) - - /** rhs.name with a pattern filter on rhs unless `pat` is irrefutable when - * matched against `rhs`. - */ - def rhsSelect(gen: GenFrom, name: TermName) = { - val rhs = if (isIrrefutableGenFrom(gen)) gen.expr else makePatFilter(gen.expr, gen.pat) - Select(rhs, name) - } - - enums match { - case (gen: GenFrom) :: Nil => - Apply(rhsSelect(gen, mapName), makeLambda(gen.pat, body)) - case (gen: GenFrom) :: (rest @ (GenFrom(_, _) :: _)) => - val cont = makeFor(mapName, flatMapName, rest, body) - Apply(rhsSelect(gen, flatMapName), makeLambda(gen.pat, cont)) - case (enum @ GenFrom(pat, rhs)) :: (rest @ GenAlias(_, _) :: _) => - val (valeqs, rest1) = rest.span(_.isInstanceOf[GenAlias]) - val pats = valeqs map { case GenAlias(pat, _) => pat } - val rhss = valeqs map { case GenAlias(_, rhs) => rhs } - val (defpat0, id0) = makeIdPat(pat) - val (defpats, ids) = (pats map makeIdPat).unzip - val pdefs = (valeqs, defpats, rhss).zipped.map(makePatDef(_, Modifiers(), _, _)) - val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, rhs) :: Nil, Block(pdefs, makeTuple(id0 :: ids))) - val allpats = pat :: pats - val vfrom1 = new IrrefutableGenFrom(makeTuple(allpats), rhs1) - makeFor(mapName, flatMapName, vfrom1 :: rest1, body) - case (gen: GenFrom) :: test :: rest => - val filtered = Apply(orFilter(rhsSelect(gen, nme.withFilter)), makeLambda(gen.pat, test)) - val genFrom = - if (isIrrefutableGenFrom(gen)) new IrrefutableGenFrom(gen.pat, filtered) - else GenFrom(gen.pat, filtered) - makeFor(mapName, flatMapName, genFrom :: rest, body) - case _ => - EmptyTree //may happen for erroneous input - } - } - - // begin desugar - tree match { - case SymbolLit(str) => - Apply( - ref(defn.SymbolClass.companionModule.termRef), - Literal(Constant(str)) :: Nil) - case InterpolatedString(id, segments) => - val strs = segments map { - case ts: Thicket => ts.trees.head - case t => t - } - val elems = segments flatMap { - case ts: Thicket => ts.trees.tail - case t => Nil - } - Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems) - case InfixOp(l, op, r) => - if (ctx.mode is Mode.Type) - if (op == tpnme.raw.AMP) AndTypeTree(l, r) // l & r - else if (op == tpnme.raw.BAR) OrTypeTree(l, r) // l | r - else AppliedTypeTree(Ident(op), l :: r :: Nil) // op[l, r] - else if (ctx.mode is Mode.Pattern) - Apply(Ident(op), l :: r :: Nil) // op(l, r) - else // l.op(r), or val x = r; l.op(x), plus handle named args specially - makeBinop(l, op, r) - case PostfixOp(t, op) => - if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) { - val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType - Annotated( - AppliedTypeTree(ref(seqType), t), - New(ref(defn.RepeatedAnnotType), Nil :: Nil)) - } else { - assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode) - Select(t, op) - } - case PrefixOp(op, t) => - Select(t, nme.UNARY_PREFIX ++ op) - case Parens(t) => - t - case Tuple(ts) => - val arity = ts.length - def tupleTypeRef = defn.TupleType(arity) - if (arity > Definitions.MaxTupleArity) { - ctx.error(TupleTooLong(ts), tree.pos) - unitLiteral - } else if (arity == 1) ts.head - else if (ctx.mode is Mode.Type) AppliedTypeTree(ref(tupleTypeRef), ts) - else if (arity == 0) unitLiteral - else Apply(ref(tupleTypeRef.classSymbol.companionModule.valRef), ts) - case WhileDo(cond, body) => - // { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() } - val call = Apply(Ident(nme.WHILE_PREFIX), Nil) - val rhs = If(cond, Block(body, call), unitLiteral) - labelDefAndCall(nme.WHILE_PREFIX, rhs, call) - case DoWhile(body, cond) => - // { label def doWhile$(): Unit = { body; if (cond) doWhile$() } ; doWhile$() } - val call = Apply(Ident(nme.DO_WHILE_PREFIX), Nil) - val rhs = Block(body, If(cond, call, unitLiteral)) - labelDefAndCall(nme.DO_WHILE_PREFIX, rhs, call) - case ForDo(enums, body) => - makeFor(nme.foreach, nme.foreach, enums, body) orElse tree - case ForYield(enums, body) => - makeFor(nme.map, nme.flatMap, enums, body) orElse tree - case PatDef(mods, pats, tpt, rhs) => - val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt)) - flatTree(pats1 map (makePatDef(tree, mods, _, rhs))) - case ParsedTry(body, handler, finalizer) => - handler match { - case Match(EmptyTree, cases) => Try(body, cases, finalizer) - case EmptyTree => Try(body, Nil, finalizer) - case _ => - Try(body, - List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))), - finalizer) - } - - } - }.withPos(tree.pos) - - /** Create a class definition with the same info as the refined type given by `parent` - * and `refinements`. - * - * parent { refinements } - * ==> - * trait <refinement> extends core { this: self => refinements } - * - * Here, `core` is the (possibly parameterized) class part of `parent`. - * If `parent` is the same as `core`, self is empty. Otherwise `self` is `parent`. - * - * Example: Given - * - * class C - * type T1 = C { type T <: A } - * - * the refined type - * - * T1 { type T <: B } - * - * is expanded to - * - * trait <refinement> extends C { this: T1 => type T <: A } - * - * The result of this method is used for validity checking, is thrown away afterwards. - * @param parent The type of `parent` - */ - def refinedTypeToClass(parent: tpd.Tree, refinements: List[Tree])(implicit ctx: Context): TypeDef = { - def stripToCore(tp: Type): List[Type] = tp match { - case tp: RefinedType if tp.argInfos.nonEmpty => tp :: Nil // parameterized class type - case tp: TypeRef if tp.symbol.isClass => tp :: Nil // monomorphic class type - case tp: TypeProxy => stripToCore(tp.underlying) - case AndType(tp1, tp2) => stripToCore(tp1) ::: stripToCore(tp2) - case _ => defn.AnyType :: Nil - } - val parentCores = stripToCore(parent.tpe) - val untpdParent = TypedSplice(parent) - val (classParents, self) = - if (parentCores.length == 1 && (parent.tpe eq parentCores.head)) (untpdParent :: Nil, EmptyValDef) - else (parentCores map TypeTree, ValDef(nme.WILDCARD, untpdParent, EmptyTree)) - val impl = Template(emptyConstructor, classParents, self, refinements) - TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait) - } - - /** If tree is a variable pattern, return its name and type, otherwise return None. - */ - private object VarPattern { - def unapply(tree: Tree)(implicit ctx: Context): Option[VarInfo] = tree match { - case id: Ident => Some(id, TypeTree()) - case Typed(id: Ident, tpt) => Some((id, tpt)) - case _ => None - } - } - - /** Returns list of all pattern variables, possibly with their types, - * without duplicates - */ - private def getVariables(tree: Tree)(implicit ctx: Context): List[VarInfo] = { - val buf = new ListBuffer[VarInfo] - def seenName(name: Name) = buf exists (_._1.name == name) - def add(named: NameTree, t: Tree): Unit = - if (!seenName(named.name)) buf += ((named, t)) - def collect(tree: Tree): Unit = tree match { - case Bind(nme.WILDCARD, tree1) => - collect(tree1) - case tree @ Bind(_, Typed(tree1, tpt)) if !mayBeTypePat(tpt) => - add(tree, tpt) - collect(tree1) - case tree @ Bind(_, tree1) => - add(tree, TypeTree()) - collect(tree1) - case Typed(id: Ident, t) if isVarPattern(id) && id.name != nme.WILDCARD && !isWildcardStarArg(tree) => - add(id, t) - case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => - add(id, TypeTree()) - case Apply(_, args) => - args foreach collect - case Typed(expr, _) => - collect(expr) - case NamedArg(_, arg) => - collect(arg) - case SeqLiteral(elems, _) => - elems foreach collect - case Alternative(trees) => - for (tree <- trees; (vble, _) <- getVariables(tree)) - ctx.error(IllegalVariableInPatternAlternative(), vble.pos) - case Annotated(arg, _) => - collect(arg) - case InterpolatedString(_, segments) => - segments foreach collect - case InfixOp(left, _, right) => - collect(left) - collect(right) - case PrefixOp(_, od) => - collect(od) - case Parens(tree) => - collect(tree) - case Tuple(trees) => - trees foreach collect - case _ => - } - collect(tree) - buf.toList - } - - private class IrrefutableGenFrom(pat: Tree, expr: Tree) extends GenFrom(pat, expr) -} diff --git a/src/dotty/tools/dotc/ast/NavigateAST.scala b/src/dotty/tools/dotc/ast/NavigateAST.scala deleted file mode 100644 index 33aa87d8e..000000000 --- a/src/dotty/tools/dotc/ast/NavigateAST.scala +++ /dev/null @@ -1,82 +0,0 @@ -package dotty.tools.dotc -package ast - -import core.Contexts.Context -import core.Decorators._ -import util.Positions._ -import Trees.{MemberDef, DefTree} - -/** Utility functions to go from typed to untyped ASTs */ -object NavigateAST { - - /** The untyped tree corresponding to typed tree `tree` in the compilation - * unit specified by `ctx` - */ - def toUntyped(tree: tpd.Tree)(implicit ctx: Context): untpd.Tree = - untypedPath(tree, exactMatch = true) match { - case (utree: untpd.Tree) :: _ => - utree - case _ => - val loosePath = untypedPath(tree, exactMatch = false) - throw new - Error(i"""no untyped tree for $tree, pos = ${tree.pos} - |best matching path =\n$loosePath%\n====\n% - |path positions = ${loosePath.map(_.pos)}""") - } - - /** The reverse path of untyped trees starting with a tree that closest matches - * `tree` and ending in the untyped tree at the root of the compilation unit - * specified by `ctx`. - * @param exactMatch If `true`, the path must start with a node that exactly - * matches `tree`, or `Nil` is returned. - * If `false` the path might start with a node enclosing - * the logical position of `tree`. - * Note: A complication concerns member definitions. ValDefs and DefDefs - * have after desugaring a position that spans just the name of the symbol being - * defined and nothing else. So we look instead for an untyped tree approximating the - * envelope of the definition, and declare success if we find another DefTree. - */ - def untypedPath(tree: tpd.Tree, exactMatch: Boolean = false)(implicit ctx: Context): List[Positioned] = - tree match { - case tree: MemberDef[_] => - untypedPath(tree.pos) match { - case path @ (last: DefTree[_]) :: _ => path - case path if !exactMatch => path - case _ => Nil - } - case _ => - untypedPath(tree.pos) match { - case (path @ last :: _) if last.pos == tree.pos || !exactMatch => path - case _ => Nil - } - } - - /** The reverse part of the untyped root of the compilation unit of `ctx` to - * position `pos`. - */ - def untypedPath(pos: Position)(implicit ctx: Context): List[Positioned] = - pathTo(pos, ctx.compilationUnit.untpdTree) - - - /** The reverse path from node `from` to the node that closest encloses position `pos`, - * or `Nil` if no such path exists. If a non-empty path is returned it starts with - * the node closest enclosing `pos` and ends with `from`. - */ - def pathTo(pos: Position, from: Positioned)(implicit ctx: Context): List[Positioned] = { - def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = { - while (it.hasNext) { - val path1 = it.next match { - case p: Positioned => singlePath(p, path) - case xs: List[_] => childPath(xs.iterator, path) - case _ => path - } - if (path1 ne path) return path1 - } - path - } - def singlePath(p: Positioned, path: List[Positioned]): List[Positioned] = - if (p.pos contains pos) childPath(p.productIterator, p :: path) - else path - singlePath(from, Nil) - } -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/ast/PluggableTransformers.scala b/src/dotty/tools/dotc/ast/PluggableTransformers.scala deleted file mode 100644 index a584230a2..000000000 --- a/src/dotty/tools/dotc/ast/PluggableTransformers.scala +++ /dev/null @@ -1,105 +0,0 @@ -package dotty.tools.dotc -package ast - - -object PluggableTransformers { -/* - import Trees._, Contexts._ - - abstract class PluggableTransformer[T] extends TreeTransformer[T, Context] { - type PluginOp[-N <: Tree[T]] = N => Tree[T] - - private[this] var _ctx: Context = _ - private[this] var _oldTree: Tree[T] = _ - - protected implicit def ctx: Context = _ctx - protected def oldTree: Tree[T] = _oldTree - protected def thisTransformer: PluggableTransformer[T] = this - - class PluginOps[-N <: Tree[T]](op: PluginOp[N], val next: Plugins) { - def apply(tree: N, old: Tree[T], c: Context): Tree[T] = { - val savedCtx = _ctx - val savedOld = _oldTree - try { - op(tree) - } finally { - _oldTree = savedOld - _ctx = savedCtx - } - } - } - - val NoOp: PluginOp[Tree[T]] = identity - val NoOps = new PluginOps(NoOp, null) - - class Plugins { - def next: Plugins = null - - def processIdent: PluginOp[Ident[T]] = NoOp - def processSelect: PluginOp[Select[T]] = NoOp - - val IdentOps: PluginOps[Ident[T]] = NoOps - val SelectOps: PluginOps[Select[T]] = NoOps - } - - val EmptyPlugin = new Plugins - - private[this] var _plugins: Plugins = EmptyPlugin - - override def plugins: Plugins = _plugins - - class Plugin extends Plugins { - override val next = _plugins - _plugins = this - - private def push[N <: Tree[T]](op: PluginOp[N], ops: => PluginOps[N]): PluginOps[N] = - if (op == NoOp) ops else new PluginOps(op, next) - - override val IdentOps: PluginOps[Ident[T]] = push(processIdent, next.IdentOps) - override val SelectOps: PluginOps[Select[T]] = push(processSelect, next.SelectOps) - } - - def postIdent(tree: Ident[T], old: Tree[T], c: Context, ops: PluginOps[Ident[T]]) = - if (ops eq NoOps) tree - else finishIdent(ops(tree, old, c), old, c, ops.next) - - override def finishIdent(tree: Tree[T], old: Tree[T], c: Context, plugins: Plugins): Tree[T] = tree match { - case tree: Ident[_] => postIdent(tree, old, c, plugins.IdentOps) - case _ => postProcess(tree, old, c, plugins) - } - - def postSelect(tree: Select[T], old: Tree[T], c: Context, ops: PluginOps[Select[T]]) = - if (ops eq NoOps) tree - else finishSelect(ops(tree, old, c), old, c, ops.next) - - override def finishSelect(tree: Tree[T], old: Tree[T], c: Context, plugins: Plugins): Tree[T] = tree match { - case tree: Select[_] => postSelect(tree, old, c, plugins.SelectOps) - case _ => postProcess(tree, old, c, plugins) - } - - protected def postProcess(tree: Tree[T], old: Tree[T], c: Context, plugins: Plugins): Tree[T] = tree match { - case tree: Ident[_] => finishIdent(tree, old, c, plugins) - case tree: Select[_] => finishSelect(tree, old, c, plugins) - } - } -} - -import PluggableTransformers._, Types._, Trees._, Contexts._ - -class ExampleTransformer extends PluggableTransformer[Type] { - - object ExamplePlugin extends Plugin { - override def processIdent = { - case tree @ Ident(x) if x.isTypeName => tree.derivedSelect(tree, x) - case tree => tpd.Ident(???) - } - override def processSelect = { tree => - if (tree.isType) tree.derivedIdent(tree.name) - else tpd.EmptyTree - } - } - - override def transform(tree: tpd.Tree, ctx: Context) = - super.transform(tree, ctx) -*/ -} diff --git a/src/dotty/tools/dotc/ast/Positioned.scala b/src/dotty/tools/dotc/ast/Positioned.scala deleted file mode 100644 index bb6817603..000000000 --- a/src/dotty/tools/dotc/ast/Positioned.scala +++ /dev/null @@ -1,213 +0,0 @@ -package dotty.tools.dotc -package ast - -import util.Positions._ -import util.DotClass -import core.Contexts.Context -import core.Decorators._ -import core.Flags.JavaDefined -import core.StdNames.nme - -/** A base class for things that have positions (currently: modifiers and trees) - */ -abstract class Positioned extends DotClass with Product { - - private[this] var curPos: Position = _ - - setPos(initialPos) - - /** The item's position. - */ - def pos: Position = curPos - - /** Destructively update `curPos` to given position. Also, set any missing - * positions in children. - */ - protected def setPos(pos: Position): Unit = { - setPosUnchecked(pos) - if (pos.exists) setChildPositions(pos.toSynthetic) - } - - /** A positioned item like this one with the position set to `pos`. - * if the positioned item is source-derived, a clone is returned. - * If the positioned item is synthetic, the position is updated - * destructively and the item itself is returned. - */ - def withPos(pos: Position): this.type = { - val newpd = (if (pos == curPos || curPos.isSynthetic) this else clone).asInstanceOf[Positioned] - newpd.setPos(pos) - newpd.asInstanceOf[this.type] - } - - def withPos(posd: Positioned): this.type = - if (posd == null) this else withPos(posd.pos) - - /** This item with a position that's the union of the given `pos` and the - * current position. - */ - def addPos(pos: Position): this.type = withPos(pos union this.pos) - - /** Set position of this tree only, without performing - * any checks of consistency with - or updates of - other positions. - * Called from Unpickler when entering positions. - */ - private[dotc] def setPosUnchecked(pos: Position) = curPos = pos - - /** If any children of this node do not have positions, - * fit their positions between the positions of the known subtrees - * and transitively visit their children. - * The method is likely time-critical because it is invoked on any node - * we create, so we want to avoid object allocations in the common case. - * The method is naturally expressed as two mutually (tail-)recursive - * functions, one which computes the next element to consider or terminates if there - * is none and the other which propagates the position information to that element. - * But since mutual tail recursion is not supported in Scala, we express it instead - * as a while loop with a termination by return in the middle. - */ - private def setChildPositions(pos: Position): Unit = { - var n = productArity // subnodes are analyzed right to left - var elems: List[Any] = Nil // children in lists still to be considered, from right to left - var end = pos.end // the last defined offset, fill in positions up to this offset - var outstanding: List[Positioned] = Nil // nodes that need their positions filled once a start position - // is known, from left to right. - def fillIn(ps: List[Positioned], start: Int, end: Int): Unit = ps match { - case p :: ps1 => - p.setPos(Position(start, end)) - fillIn(ps1, end, end) - case nil => - } - while (true) { - var nextChild: Any = null // the next child to be considered - if (elems.nonEmpty) { - nextChild = elems.head - elems = elems.tail - } - else if (n > 0) { - n = n - 1 - nextChild = productElement(n) - } - else { - fillIn(outstanding, pos.start, end) - return - } - nextChild match { - case p: Positioned => - if (p.pos.exists) { - fillIn(outstanding, p.pos.end, end) - outstanding = Nil - end = p.pos.start - } - else outstanding = p :: outstanding - case xs: List[_] => - elems = elems ::: xs.reverse - case _ => - } - } - } - - /** The initial, synthetic position. This is usually the union of all positioned children's positions. - */ - def initialPos: Position = { - var n = productArity - var pos = NoPosition - while (n > 0) { - n -= 1 - productElement(n) match { - case p: Positioned => pos = pos union p.pos - case xs: List[_] => pos = unionPos(pos, xs) - case _ => - } - } - pos.toSynthetic - } - - private def unionPos(pos: Position, xs: List[_]): Position = xs match { - case (p: Positioned) :: xs1 => unionPos(pos union p.pos, xs1) - case _ => pos - } - - def contains(that: Positioned): Boolean = { - def isParent(x: Any): Boolean = x match { - case x: Positioned => - x contains that - case xs: List[_] => - xs exists isParent - case _ => - false - } - (this eq that) || - (this.pos contains that.pos) && { - var n = productArity - var found = false - while (!found && n > 0) { - n -= 1 - found = isParent(productElement(n)) - } - found - } - } - - /** Check that all positioned items in this tree satisfy the following conditions: - * - Parent positions contain child positions - * - If item is a non-empty tree, it has a position - */ - def checkPos(nonOverlapping: Boolean)(implicit ctx: Context): Unit = try { - import untpd._ - var lastPositioned: Positioned = null - var lastPos = NoPosition - def check(p: Any): Unit = p match { - case p: Positioned => - assert(pos contains p.pos, - s"""position error, parent position does not contain child positon - |parent = $this, - |parent position = $pos, - |child = $p, - |child position = ${p.pos}""".stripMargin) - p match { - case tree: Tree if !tree.isEmpty => - assert(tree.pos.exists, - s"position error: position not set for $tree # ${tree.uniqueId}") - case _ => - } - if (nonOverlapping) { - this match { - case _: WildcardFunction - if lastPositioned.isInstanceOf[ValDef] && !p.isInstanceOf[ValDef] => - // ignore transition from last wildcard parameter to body - case _ => - assert(!lastPos.exists || !p.pos.exists || lastPos.end <= p.pos.start, - s"""position error, child positions overlap or in wrong order - |parent = $this - |1st child = $lastPositioned - |1st child position = $lastPos - |2nd child = $p - |2nd child position = ${p.pos}""".stripMargin) - } - lastPositioned = p - lastPos = p.pos - } - p.checkPos(nonOverlapping) - case xs: List[_] => - xs.foreach(check) - case _ => - } - this match { - case tree: DefDef if tree.name == nme.CONSTRUCTOR && tree.mods.is(JavaDefined) => - // Special treatment for constructors coming from Java: - // Leave out tparams, they are copied with wrong positions from parent class - check(tree.mods) - check(tree.vparamss) - case _ => - val end = productArity - var n = 0 - while (n < end) { - check(productElement(n)) - n += 1 - } - } - } catch { - case ex: AssertionError => - println(i"error while checking $this") - throw ex - } -} diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala deleted file mode 100644 index d1e6bd38a..000000000 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ /dev/null @@ -1,733 +0,0 @@ -package dotty.tools -package dotc -package ast - -import core._ -import Flags._, Trees._, Types._, Contexts._ -import Names._, StdNames._, NameOps._, Decorators._, Symbols._ -import util.HashSet -import typer.ConstFold - -trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => - import TreeInfo._ - - // Note: the <: Type constraint looks necessary (and is needed to make the file compile in dotc). - // But Scalac accepts the program happily without it. Need to find out why. - - def unsplice[T >: Untyped](tree: Trees.Tree[T]): Trees.Tree[T] = tree.asInstanceOf[untpd.Tree] match { - case untpd.TypedSplice(tree1) => tree1.asInstanceOf[Trees.Tree[T]] - case _ => tree - } - - def isDeclarationOrTypeDef(tree: Tree): Boolean = unsplice(tree) match { - case DefDef(_, _, _, _, EmptyTree) - | ValDef(_, _, EmptyTree) - | TypeDef(_, _) => true - case _ => false - } - - /** The largest subset of {NoInits, PureInterface} that a - * trait enclosing this statement can have as flags. - * Does tree contain an initialization part when seen as a member of a class or trait? - */ - def defKind(tree: Tree): FlagSet = unsplice(tree) match { - case EmptyTree | _: Import => NoInitsInterface - case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface - case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits - case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags - case _ => EmptyFlags - } - - def isOpAssign(tree: Tree) = unsplice(tree) match { - case Apply(fn, _ :: _) => - unsplice(fn) match { - case Select(_, name) if name.isOpAssignmentName => true - case _ => false - } - case _ => false - } - - class MatchingArgs(params: List[Symbol], args: List[Tree])(implicit ctx: Context) { - def foreach(f: (Symbol, Tree) => Unit): Boolean = { - def recur(params: List[Symbol], args: List[Tree]): Boolean = params match { - case Nil => args.isEmpty - case param :: params1 => - if (param.info.isRepeatedParam) { - for (arg <- args) f(param, arg) - true - } else args match { - case Nil => false - case arg :: args1 => - f(param, args.head) - recur(params1, args1) - } - } - recur(params, args) - } - def zipped: List[(Symbol, Tree)] = map((_, _)) - def map[R](f: (Symbol, Tree) => R): List[R] = { - val b = List.newBuilder[R] - foreach(b += f(_, _)) - b.result - } - } - - /** The method part of an application node, possibly enclosed in a block - * with only valdefs as statements. the reason for also considering blocks - * is that named arguments can transform a call into a block, e.g. - * <init>(b = foo, a = bar) - * is transformed to - * { val x$1 = foo - * val x$2 = bar - * <init>(x$2, x$1) - * } - */ - def methPart(tree: Tree): Tree = stripApply(tree) match { - case TypeApply(fn, _) => methPart(fn) - case AppliedTypeTree(fn, _) => methPart(fn) // !!! should not be needed - case Block(stats, expr) => methPart(expr) - case mp => mp - } - - /** If this is an application, its function part, stripping all - * Apply nodes (but leaving TypeApply nodes in). Otherwise the tree itself. - */ - def stripApply(tree: Tree): Tree = unsplice(tree) match { - case Apply(fn, _) => stripApply(fn) - case _ => tree - } - - /** The number of arguments in an application */ - def numArgs(tree: Tree): Int = unsplice(tree) match { - case Apply(fn, args) => numArgs(fn) + args.length - case TypeApply(fn, _) => numArgs(fn) - case Block(_, expr) => numArgs(expr) - case _ => 0 - } - - /** The (last) list of arguments of an application */ - def arguments(tree: Tree): List[Tree] = unsplice(tree) match { - case Apply(_, args) => args - case TypeApply(fn, _) => arguments(fn) - case Block(_, expr) => arguments(expr) - case _ => Nil - } - - /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the - * same object? - */ - def isSelfConstrCall(tree: Tree): Boolean = methPart(tree) match { - case Ident(nme.CONSTRUCTOR) | Select(This(_), nme.CONSTRUCTOR) => true - case _ => false - } - - /** Is tree a super constructor call? - */ - def isSuperConstrCall(tree: Tree): Boolean = methPart(tree) match { - case Select(Super(_, _), nme.CONSTRUCTOR) => true - case _ => false - } - - def isSuperSelection(tree: untpd.Tree) = unsplice(tree) match { - case Select(Super(_, _), _) => true - case _ => false - } - - def isSelfOrSuperConstrCall(tree: Tree): Boolean = methPart(tree) match { - case Ident(nme.CONSTRUCTOR) - | Select(This(_), nme.CONSTRUCTOR) - | Select(Super(_, _), nme.CONSTRUCTOR) => true - case _ => false - } - - /** Is tree a variable pattern? */ - def isVarPattern(pat: untpd.Tree): Boolean = unsplice(pat) match { - case x: BackquotedIdent => false - case x: Ident => x.name.isVariableName - case _ => false - } - - /** The first constructor definition in `stats` */ - def firstConstructor(stats: List[Tree]): Tree = stats match { - case (meth: DefDef) :: _ if meth.name.isConstructorName => meth - case stat :: stats => firstConstructor(stats) - case nil => EmptyTree - } - - /** The arguments to the first constructor in `stats`. */ - def firstConstructorArgs(stats: List[Tree]): List[Tree] = firstConstructor(stats) match { - case DefDef(_, _, args :: _, _, _) => args - case _ => Nil - } - - /** Is tpt a vararg type of the form T* or => T*? */ - def isRepeatedParamType(tpt: Tree)(implicit ctx: Context): Boolean = tpt match { - case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1) - case tpt: TypeTree => tpt.typeOpt.isRepeatedParam - case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true - case _ => false - } - - /** Is name a left-associative operator? */ - def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.last != ':') - - /** can this type be a type pattern? */ - def mayBeTypePat(tree: untpd.Tree): Boolean = unsplice(tree) match { - case AndTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) - case OrTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) - case RefinedTypeTree(tpt, refinements) => mayBeTypePat(tpt) || refinements.exists(_.isInstanceOf[Bind]) - case AppliedTypeTree(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind]) - case Select(tpt, _) => mayBeTypePat(tpt) - case Annotated(tpt, _) => mayBeTypePat(tpt) - case _ => false - } - - /** Is this argument node of the form <expr> : _* ? - */ - def isWildcardStarArg(tree: Tree)(implicit ctx: Context): Boolean = unbind(tree) match { - case Typed(Ident(nme.WILDCARD_STAR), _) => true - case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true - case Typed(_, tpt: TypeTree) => tpt.hasType && tpt.tpe.isRepeatedParam - case _ => false - } - - /** If this tree has type parameters, those. Otherwise Nil. - def typeParameters(tree: Tree): List[TypeDef] = tree match { - case DefDef(_, _, tparams, _, _, _) => tparams - case ClassDef(_, _, tparams, _) => tparams - case TypeDef(_, _, tparams, _) => tparams - case _ => Nil - }*/ - - /** Does this argument list end with an argument of the form <expr> : _* ? */ - def isWildcardStarArgList(trees: List[Tree])(implicit ctx: Context) = - trees.nonEmpty && isWildcardStarArg(trees.last) - - /** Is the argument a wildcard argument of the form `_` or `x @ _`? - */ - def isWildcardArg(tree: Tree): Boolean = unbind(tree) match { - case Ident(nme.WILDCARD) => true - case _ => false - } - - /** Does this list contain a named argument tree? */ - def hasNamedArg(args: List[Any]) = args exists isNamedArg - val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]] - - /** Is this pattern node a catch-all (wildcard or variable) pattern? */ - def isDefaultCase(cdef: CaseDef) = cdef match { - case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat) - case _ => false - } - - /** Is this pattern node a synthetic catch-all case, added during PartialFuction synthesis before we know - * whether the user provided cases are exhaustive. */ - def isSyntheticDefaultCase(cdef: CaseDef) = unsplice(cdef) match { - case CaseDef(Bind(nme.DEFAULT_CASE, _), EmptyTree, _) => true - case _ => false - } - - /** Does this CaseDef catch Throwable? */ - def catchesThrowable(cdef: CaseDef)(implicit ctx: Context) = - catchesAllOf(cdef, defn.ThrowableType) - - /** Does this CaseDef catch everything of a certain Type? */ - def catchesAllOf(cdef: CaseDef, threshold: Type)(implicit ctx: Context) = - isDefaultCase(cdef) || - cdef.guard.isEmpty && { - unbind(cdef.pat) match { - case Typed(Ident(nme.WILDCARD), tpt) => threshold <:< tpt.typeOpt - case _ => false - } - } - - /** Is this case guarded? */ - def isGuardedCase(cdef: CaseDef) = cdef.guard ne EmptyTree - - /** The underlying pattern ignoring any bindings */ - def unbind(x: Tree): Tree = unsplice(x) match { - case Bind(_, y) => unbind(y) - case y => y - } - - /** Checks whether predicate `p` is true for all result parts of this expression, - * where we zoom into Ifs, Matches, and Blocks. - */ - def forallResults(tree: Tree, p: Tree => Boolean): Boolean = tree match { - case If(_, thenp, elsep) => forallResults(thenp, p) && forallResults(elsep, p) - case Match(_, cases) => cases forall (c => forallResults(c.body, p)) - case Block(_, expr) => forallResults(expr, p) - case _ => p(tree) - } -} - -trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] => - import TreeInfo._ - import untpd._ - - /** True iff definition is a val or def with no right-hand-side, or it - * is an abstract typoe declaration - */ - def lacksDefinition(mdef: MemberDef)(implicit ctx: Context) = mdef match { - case mdef: ValOrDefDef => - mdef.unforcedRhs == EmptyTree && !mdef.name.isConstructorName && !mdef.mods.is(ParamAccessor) - case mdef: TypeDef => - def isBounds(rhs: Tree): Boolean = rhs match { - case _: TypeBoundsTree => true - case PolyTypeTree(_, body) => isBounds(body) - case _ => false - } - mdef.rhs.isEmpty || isBounds(mdef.rhs) - case _ => false - } - - def isFunctionWithUnknownParamType(tree: Tree) = tree match { - case Function(args, _) => - args.exists { - case ValDef(_, tpt, _) => tpt.isEmpty - case _ => false - } - case _ => false - } - - // todo: fill with other methods from TreeInfo that only apply to untpd.Tree's -} - -trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => - import TreeInfo._ - import tpd._ - - /** The purity level of this statement. - * @return pure if statement has no side effects - * idempotent if running the statement a second time has no side effects - * impure otherwise - */ - private def statPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { - case EmptyTree - | TypeDef(_, _) - | Import(_, _) - | DefDef(_, _, _, _, _) => - Pure - case vdef @ ValDef(_, _, _) => - if (vdef.symbol.flags is Mutable) Impure else exprPurity(vdef.rhs) - case _ => - Impure - // TODO: It seem like this should be exprPurity(tree) - // But if we do that the repl/vars test break. Need to figure out why that's the case. - } - - /** The purity level of this expression. - * @return pure if expression has no side effects - * idempotent if running the expression a second time has no side effects - * impure otherwise - * - * Note that purity and idempotency are different. References to modules and lazy - * vals are impure (side-effecting) both because side-effecting code may be executed and because the first reference - * takes a different code path than all to follow; but they are idempotent - * because running the expression a second time gives the cached result. - */ - private def exprPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { - case EmptyTree - | This(_) - | Super(_, _) - | Literal(_) - | Closure(_, _, _) => - Pure - case Ident(_) => - refPurity(tree) - case Select(qual, _) => - refPurity(tree).min(exprPurity(qual)) - case TypeApply(fn, _) => - exprPurity(fn) -/* - * Not sure we'll need that. Comment out until we find out - case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => - // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm` - free.symbol.hasStableFlag && isIdempotentExpr(free) -*/ - case Apply(fn, args) => - def isKnownPureOp(sym: Symbol) = - sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass - // Note: After uncurry, field accesses are represented as Apply(getter, Nil), - // so an Apply can also be pure. - if (args.isEmpty && fn.symbol.is(Stable)) exprPurity(fn) - else if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol)) - // A constant expression with pure arguments is pure. - minOf(exprPurity(fn), args.map(exprPurity)) - else Impure - case Typed(expr, _) => - exprPurity(expr) - case Block(stats, expr) => - minOf(exprPurity(expr), stats.map(statPurity)) - case _ => - Impure - } - - private def minOf(l0: PurityLevel, ls: List[PurityLevel]) = (l0 /: ls)(_ min _) - - def isPureExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) == Pure - def isIdempotentExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) >= Idempotent - - /** The purity level of this reference. - * @return - * pure if reference is (nonlazy and stable) or to a parameterized function - * idempotent if reference is lazy and stable - * impure otherwise - * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable - * flags set. - */ - private def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = - if (!tree.tpe.widen.isParameterless) Pure - else if (!tree.symbol.isStable) Impure - else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start. - else Pure - - def isPureRef(tree: Tree)(implicit ctx: Context) = - refPurity(tree) == Pure - def isIdempotentRef(tree: Tree)(implicit ctx: Context) = - refPurity(tree) >= Idempotent - - /** If `tree` is a constant expression, its value as a Literal, - * or `tree` itself otherwise. - * - * Note: Demanding idempotency instead of purity in literalize is strictly speaking too loose. - * Example - * - * object O { final val x = 42; println("43") } - * O.x - * - * Strictly speaking we can't replace `O.x` with `42`. But this would make - * most expressions non-constant. Maybe we can change the spec to accept this - * kind of eliding behavior. Or else enforce true purity in the compiler. - * The choice will be affected by what we will do with `inline` and with - * Singleton type bounds (see SIP 23). Presumably - * - * object O1 { val x: Singleton = 42; println("43") } - * object O2 { inline val x = 42; println("43") } - * - * should behave differently. - * - * O1.x should have the same effect as { println("43"); 42 } - * - * whereas - * - * O2.x = 42 - * - * Revisit this issue once we have implemented `inline`. Then we can demand - * purity of the prefix unless the selection goes to an inline val. - * - * Note: This method should be applied to all term tree nodes that are not literals, - * that can be idempotent, and that can have constant types. So far, only nodes - * of the following classes qualify: - * - * Ident - * Select - * TypeApply - */ - def constToLiteral(tree: Tree)(implicit ctx: Context): Tree = { - val tree1 = ConstFold(tree) - tree1.tpe.widenTermRefExpr match { - case ConstantType(value) if isIdempotentExpr(tree1) => Literal(value) - case _ => tree1 - } - } - - /** Is symbol potentially a getter of a mutable variable? - */ - def mayBeVarGetter(sym: Symbol)(implicit ctx: Context): Boolean = { - def maybeGetterType(tpe: Type): Boolean = tpe match { - case _: ExprType | _: ImplicitMethodType => true - case tpe: PolyType => maybeGetterType(tpe.resultType) - case _ => false - } - sym.owner.isClass && !sym.isStable && maybeGetterType(sym.info) - } - - /** Is tree a reference to a mutable variable, or to a potential getter - * that has a setter in the same class? - */ - def isVariableOrGetter(tree: Tree)(implicit ctx: Context) = { - def sym = tree.symbol - def isVar = sym is Mutable - def isGetter = - mayBeVarGetter(sym) && sym.owner.info.member(sym.name.asTermName.setterName).exists - - unsplice(tree) match { - case Ident(_) => isVar - case Select(_, _) => isVar || isGetter - case Apply(_, _) => - methPart(tree) match { - case Select(qual, nme.apply) => qual.tpe.member(nme.update).exists - case _ => false - } - case _ => false - } - } - - /** Is tree a `this` node which belongs to `enclClass`? */ - def isSelf(tree: Tree, enclClass: Symbol)(implicit ctx: Context): Boolean = unsplice(tree) match { - case This(_) => tree.symbol == enclClass - case _ => false - } - - /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */ - def stripCast(tree: Tree)(implicit ctx: Context): Tree = { - def isCast(sel: Tree) = sel.symbol == defn.Any_asInstanceOf - unsplice(tree) match { - case TypeApply(sel @ Select(inner, _), _) if isCast(sel) => - stripCast(inner) - case Apply(TypeApply(sel @ Select(inner, _), _), Nil) if isCast(sel) => - stripCast(inner) - case t => - t - } - } - - /** Decompose a call fn[targs](vargs_1)...(vargs_n) - * into its constituents (where targs, vargss may be empty) - */ - def decomposeCall(tree: Tree): (Tree, List[Tree], List[List[Tree]]) = tree match { - case Apply(fn, args) => - val (meth, targs, argss) = decomposeCall(fn) - (meth, targs, argss :+ args) - case TypeApply(fn, targs) => - val (meth, Nil, Nil) = decomposeCall(fn) - (meth, targs, Nil) - case _ => - (tree, Nil, Nil) - } - - /** An extractor for closures, either contained in a block or standalone. - */ - object closure { - def unapply(tree: Tree): Option[(List[Tree], Tree, Tree)] = tree match { - case Block(_, Closure(env, meth, tpt)) => Some(env, meth, tpt) - case Closure(env, meth, tpt) => Some(env, meth, tpt) - case _ => None - } - } - - /** If tree is a closure, its body, otherwise tree itself */ - def closureBody(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs - case _ => tree - } - - /** The variables defined by a pattern, in reverse order of their appearance. */ - def patVars(tree: Tree)(implicit ctx: Context): List[Symbol] = { - val acc = new TreeAccumulator[List[Symbol]] { - def apply(syms: List[Symbol], tree: Tree)(implicit ctx: Context) = tree match { - case Bind(_, body) => apply(tree.symbol :: syms, body) - case _ => foldOver(syms, tree) - } - } - acc(Nil, tree) - } - - /** Is this pattern node a catch-all or type-test pattern? */ - def isCatchCase(cdef: CaseDef)(implicit ctx: Context) = cdef match { - case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) => - isSimpleThrowable(tpt.tpe) - case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) => - isSimpleThrowable(tpt.tpe) - case _ => - isDefaultCase(cdef) - } - - private def isSimpleThrowable(tp: Type)(implicit ctx: Context): Boolean = tp match { - case tp @ TypeRef(pre, _) => - (pre == NoPrefix || pre.widen.typeSymbol.isStatic) && - (tp.symbol derivesFrom defn.ThrowableClass) && !(tp.symbol is Trait) - case _ => - false - } - - /** The symbols defined locally in a statement list */ - def localSyms(stats: List[Tree])(implicit ctx: Context): List[Symbol] = - for (stat <- stats if stat.isDef && stat.symbol.exists) yield stat.symbol - - /** If `tree` is a DefTree, the symbol defined by it, otherwise NoSymbol */ - def definedSym(tree: Tree)(implicit ctx: Context): Symbol = - if (tree.isDef) tree.symbol else NoSymbol - - /** Going from child to parent, the path of tree nodes that starts - * with a definition of symbol `sym` and ends with `root`, or Nil - * if no such path exists. - * Pre: `sym` must have a position. - */ - def defPath(sym: Symbol, root: Tree)(implicit ctx: Context): List[Tree] = ctx.debugTraceIndented(s"defpath($sym with position ${sym.pos}, ${root.show})") { - require(sym.pos.exists) - object accum extends TreeAccumulator[List[Tree]] { - def apply(x: List[Tree], tree: Tree)(implicit ctx: Context): List[Tree] = { - if (tree.pos.contains(sym.pos)) - if (definedSym(tree) == sym) tree :: x - else { - val x1 = foldOver(x, tree) - if (x1 ne x) tree :: x1 else x1 - } - else x - } - } - accum(Nil, root) - } - - - /** The top level classes in this tree, including only those module classes that - * are not a linked class of some other class in the result. - */ - def topLevelClasses(tree: Tree)(implicit ctx: Context): List[ClassSymbol] = tree match { - case PackageDef(_, stats) => stats.flatMap(topLevelClasses) - case tdef: TypeDef if tdef.symbol.isClass => tdef.symbol.asClass :: Nil - case _ => Nil - } - - /** The tree containing only the top-level classes and objects matching either `cls` or its companion object */ - def sliceTopLevel(tree: Tree, cls: ClassSymbol)(implicit ctx: Context): List[Tree] = tree match { - case PackageDef(pid, stats) => - cpy.PackageDef(tree)(pid, stats.flatMap(sliceTopLevel(_, cls))) :: Nil - case tdef: TypeDef => - val sym = tdef.symbol - assert(sym.isClass) - if (cls == sym || cls == sym.linkedClass) tdef :: Nil - else Nil - case vdef: ValDef => - val sym = vdef.symbol - assert(sym is Module) - if (cls == sym.companionClass || cls == sym.moduleClass) vdef :: Nil - else Nil - case tree => - tree :: Nil - } - - /** The statement sequence that contains a definition of `sym`, or Nil - * if none was found. - * For a tree to be found, The symbol must have a position and its definition - * tree must be reachable from come tree stored in an enclosing context. - */ - def definingStats(sym: Symbol)(implicit ctx: Context): List[Tree] = - if (!sym.pos.exists || (ctx eq NoContext) || ctx.compilationUnit == null) Nil - else defPath(sym, ctx.compilationUnit.tpdTree) match { - case defn :: encl :: _ => - def verify(stats: List[Tree]) = - if (stats exists (definedSym(_) == sym)) stats else Nil - encl match { - case Block(stats, _) => verify(stats) - case encl: Template => verify(encl.body) - case PackageDef(_, stats) => verify(stats) - case _ => Nil - } - case nil => - Nil - } -} - -object TreeInfo { - class PurityLevel(val x: Int) extends AnyVal { - def >= (that: PurityLevel) = x >= that.x - def min(that: PurityLevel) = new PurityLevel(x min that.x) - } - - val Pure = new PurityLevel(2) - val Idempotent = new PurityLevel(1) - val Impure = new PurityLevel(0) -} - - /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) - def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation defn.SwitchClass - */ - - /** Does list of trees start with a definition of - * a class of module with given name (ignoring imports) - def firstDefinesClassOrObject(trees: List[Tree], name: Name): Boolean = trees match { - case Import(_, _) :: xs => firstDefinesClassOrObject(xs, name) - case Annotated(_, tree1) :: Nil => firstDefinesClassOrObject(List(tree1), name) - case ModuleDef(_, `name`, _) :: Nil => true - case ClassDef(_, `name`, _, _) :: Nil => true - case _ => false - } - - - /** Is this file the body of a compilation unit which should not - * have Predef imported? - */ - def noPredefImportForUnit(body: Tree) = { - // Top-level definition whose leading imports include Predef. - def isLeadingPredefImport(defn: Tree): Boolean = defn match { - case PackageDef(_, defs1) => defs1 exists isLeadingPredefImport - case Import(expr, _) => isReferenceToPredef(expr) - case _ => false - } - // Compilation unit is class or object 'name' in package 'scala' - def isUnitInScala(tree: Tree, name: Name) = tree match { - case PackageDef(Ident(nme.scala_), defs) => firstDefinesClassOrObject(defs, name) - case _ => false - } - - isUnitInScala(body, nme.Predef) || isLeadingPredefImport(body) - } - */ - - /* - def isAbsTypeDef(tree: Tree) = tree match { - case TypeDef(_, _, _, TypeBoundsTree(_, _)) => true - case TypeDef(_, _, _, rhs) => rhs.tpe.isInstanceOf[TypeBounds] - case _ => false - } - - def isAliasTypeDef(tree: Tree) = tree match { - case TypeDef(_, _, _, _) => !isAbsTypeDef(tree) - case _ => false - } - - /** Some handy extractors for spotting trees through the - * the haze of irrelevant braces: i.e. Block(Nil, SomeTree) - * should not keep us from seeing SomeTree. - */ - abstract class SeeThroughBlocks[T] { - protected def unapplyImpl(x: Tree): T - def unapply(x: Tree): T = x match { - case Block(Nil, expr) => unapply(expr) - case _ => unapplyImpl(x) - } - } - object IsTrue extends SeeThroughBlocks[Boolean] { - protected def unapplyImpl(x: Tree): Boolean = x match { - case Literal(Constant(true)) => true - case _ => false - } - } - object IsFalse extends SeeThroughBlocks[Boolean] { - protected def unapplyImpl(x: Tree): Boolean = x match { - case Literal(Constant(false)) => true - case _ => false - } - } - object IsIf extends SeeThroughBlocks[Option[(Tree, Tree, Tree)]] { - protected def unapplyImpl(x: Tree) = x match { - case If(cond, thenp, elsep) => Some((cond, thenp, elsep)) - case _ => None - } - } - - object MacroImplReference { - private def refPart(tree: Tree): Tree = tree match { - case TypeApply(fun, _) => refPart(fun) - case ref: RefTree => ref - case _ => EmptyTree() - } - - def unapply(tree: Tree) = refPart(tree) match { - case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, dissectApplied(tree).targs)) - case _ => None - } - } - - def isNullaryInvocation(tree: Tree): Boolean = - tree.symbol != null && tree.symbol.isMethod && (tree match { - case TypeApply(fun, _) => isNullaryInvocation(fun) - case tree: RefTree => true - case _ => false - })*/ - - - diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala deleted file mode 100644 index cf529dfda..000000000 --- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ /dev/null @@ -1,187 +0,0 @@ -package dotty.tools -package dotc -package ast - -import core._ -import Types._, Contexts._, Constants._, Names._, Flags._ -import SymDenotations._, Symbols._, Annotations._, Trees._, Symbols._ -import Denotations._, Decorators._ -import dotty.tools.dotc.transform.SymUtils._ - -/** A map that applies three functions and a substitution together to a tree and - * makes sure they are coordinated so that the result is well-typed. The functions are - * @param typeMap A function from Type to Type that gets applied to the - * type of every tree node and to all locally defined symbols, - * followed by the substitution [substFrom := substTo]. - * @param treeMap A transformer that translates all encountered subtrees in - * prefix traversal orders - * @param oldOwners Previous owners. If a top-level local symbol in the mapped tree - * has one of these as an owner, the owner is replaced by the corresponding - * symbol in `newOwners`. - * @param newOwners New owners, replacing previous owners. - * @param substFrom The symbols that need to be substituted. - * @param substTo The substitution targets. - * - * The reason the substitution is broken out from the rest of the type map is - * that all symbols have to be substituted at the same time. If we do not do this, - * we risk data races on named types. Example: Say we have `outer#1.inner#2` and we - * have two substitutions S1 = [outer#1 := outer#3], S2 = [inner#2 := inner#4] where - * hashtags precede symbol ids. If we do S1 first, we get outer#2.inner#3. If we then - * do S2 we get outer#2.inner#4. But that means that the named type outer#2.inner - * gets two different denotations in the same period. Hence, if -Yno-double-bindings is - * set, we would get a data race assertion error. - */ -final class TreeTypeMap( - val typeMap: Type => Type = IdentityTypeMap, - val treeMap: tpd.Tree => tpd.Tree = identity _, - val oldOwners: List[Symbol] = Nil, - val newOwners: List[Symbol] = Nil, - val substFrom: List[Symbol] = Nil, - val substTo: List[Symbol] = Nil)(implicit ctx: Context) extends tpd.TreeMap { - import tpd._ - - /** If `sym` is one of `oldOwners`, replace by corresponding symbol in `newOwners` */ - def mapOwner(sym: Symbol) = sym.subst(oldOwners, newOwners) - - /** Replace occurrences of `This(oldOwner)` in some prefix of a type - * by the corresponding `This(newOwner)`. - */ - private val mapOwnerThis = new TypeMap { - private def mapPrefix(from: List[Symbol], to: List[Symbol], tp: Type): Type = from match { - case Nil => tp - case (cls: ClassSymbol) :: from1 => mapPrefix(from1, to.tail, tp.substThis(cls, to.head.thisType)) - case _ :: from1 => mapPrefix(from1, to.tail, tp) - } - def apply(tp: Type): Type = tp match { - case tp: NamedType => tp.derivedSelect(mapPrefix(oldOwners, newOwners, tp.prefix)) - case _ => mapOver(tp) - } - } - - def mapType(tp: Type) = - mapOwnerThis(typeMap(tp).substSym(substFrom, substTo)) - - private def updateDecls(prevStats: List[Tree], newStats: List[Tree]): Unit = - if (prevStats.isEmpty) assert(newStats.isEmpty) - else { - prevStats.head match { - case pdef: MemberDef => - val prevSym = pdef.symbol - val newSym = newStats.head.symbol - val newCls = newSym.owner.asClass - if (prevSym != newSym) newCls.replace(prevSym, newSym) - case _ => - } - updateDecls(prevStats.tail, newStats.tail) - } - - override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = treeMap(tree) match { - case impl @ Template(constr, parents, self, _) => - val tmap = withMappedSyms(localSyms(impl :: self :: Nil)) - cpy.Template(impl)( - constr = tmap.transformSub(constr), - parents = parents mapconserve transform, - self = tmap.transformSub(self), - body = impl.body mapconserve - (tmap.transform(_)(ctx.withOwner(mapOwner(impl.symbol.owner)))) - ).withType(tmap.mapType(impl.tpe)) - case tree1 => - tree1.withType(mapType(tree1.tpe)) match { - case id: Ident if tpd.needsSelect(id.tpe) => - ref(id.tpe.asInstanceOf[TermRef]).withPos(id.pos) - case ddef @ DefDef(name, tparams, vparamss, tpt, _) => - val (tmap1, tparams1) = transformDefs(ddef.tparams) - val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss) - val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs)) - res.symbol.transformAnnotations { - case ann: BodyAnnotation => ann.derivedAnnotation(res.rhs) - case ann => ann - } - res - case blk @ Block(stats, expr) => - val (tmap1, stats1) = transformDefs(stats) - val expr1 = tmap1.transform(expr) - cpy.Block(blk)(stats1, expr1) - case inlined @ Inlined(call, bindings, expanded) => - val (tmap1, bindings1) = transformDefs(bindings) - val expanded1 = tmap1.transform(expanded) - cpy.Inlined(inlined)(call, bindings1, expanded1) - case cdef @ CaseDef(pat, guard, rhs) => - val tmap = withMappedSyms(patVars(pat)) - val pat1 = tmap.transform(pat) - val guard1 = tmap.transform(guard) - val rhs1 = tmap.transform(rhs) - cpy.CaseDef(cdef)(pat1, guard1, rhs1) - case tree1 => - super.transform(tree1) - } - } - - override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) = - transformDefs(trees)._2 - - private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = { - val tmap = withMappedSyms(tpd.localSyms(trees)) - (tmap, tmap.transformSub(trees)) - } - - private def transformVParamss(vparamss: List[List[ValDef]]): (TreeTypeMap, List[List[ValDef]]) = vparamss match { - case vparams :: rest => - val (tmap1, vparams1) = transformDefs(vparams) - val (tmap2, vparamss2) = tmap1.transformVParamss(rest) - (tmap2, vparams1 :: vparamss2) - case nil => - (this, vparamss) - } - - def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree] - - def apply(annot: Annotation): Annotation = annot.derivedAnnotation(apply(annot.tree)) - - /** The current tree map composed with a substitution [from -> to] */ - def withSubstitution(from: List[Symbol], to: List[Symbol]): TreeTypeMap = - if (from eq to) this - else { - // assert that substitution stays idempotent, assuming its parts are - // TODO: It might be better to cater for the asserted-away conditions, by - // setting up a proper substitution abstraction with a compose operator that - // guarantees idempotence. But this might be too inefficient in some cases. - // We'll cross that bridge when we need to. - assert(!from.exists(substTo contains _)) - assert(!to.exists(substFrom contains _)) - assert(!from.exists(newOwners contains _)) - assert(!to.exists(oldOwners contains _)) - new TreeTypeMap( - typeMap, - treeMap, - from ++ oldOwners, - to ++ newOwners, - from ++ substFrom, - to ++ substTo) - } - - /** Apply `typeMap` and `ownerMap` to given symbols `syms` - * and return a treemap that contains the substitution - * between original and mapped symbols. - */ - def withMappedSyms(syms: List[Symbol], mapAlways: Boolean = false): TreeTypeMap = - withMappedSyms(syms, ctx.mapSymbols(syms, this, mapAlways)) - - /** The tree map with the substitution between originals `syms` - * and mapped symbols `mapped`. Also goes into mapped classes - * and substitutes their declarations. - */ - def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = { - val symsChanged = syms ne mapped - val substMap = withSubstitution(syms, mapped) - val fullMap = (substMap /: mapped.filter(_.isClass)) { (tmap, cls) => - val origDcls = cls.info.decls.toList - val mappedDcls = ctx.mapSymbols(origDcls, tmap) - val tmap1 = tmap.withMappedSyms(origDcls, mappedDcls) - if (symsChanged) (origDcls, mappedDcls).zipped.foreach(cls.asClass.replace) - tmap1 - } - if (symsChanged || (fullMap eq substMap)) fullMap - else withMappedSyms(syms, mapAlways = true) - } -} diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala deleted file mode 100644 index 2801bcae2..000000000 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ /dev/null @@ -1,1295 +0,0 @@ -package dotty.tools -package dotc -package ast - -import core._ -import Types._, Names._, Flags._, util.Positions._, Contexts._, Constants._ -import SymDenotations._, Symbols._, Denotations._, StdNames._, Comments._ -import annotation.tailrec -import language.higherKinds -import collection.IndexedSeqOptimized -import collection.immutable.IndexedSeq -import collection.mutable.ListBuffer -import parsing.Tokens.Token -import printing.Printer -import util.{Stats, Attachment, Property, DotClass} -import annotation.unchecked.uncheckedVariance -import language.implicitConversions - -object Trees { - - // Note: it would be more logical to make Untyped = Nothing. - // However, this interacts in a bad way with Scala's current type inference. - // In fact, we cannot write something like Select(pre, name), where pre is - // of type Tree[Nothing]; type inference will treat the Nothing as an uninstantiated - // value and will not infer Nothing as the type parameter for Select. - // We should come back to this issue once type inference is changed. - type Untyped = Null - - /** The total number of created tree nodes, maintained if Stats.enabled */ - @sharable var ntrees = 0 - - /** Property key for trees with documentation strings attached */ - val DocComment = new Property.Key[Comment] - - @sharable private var nextId = 0 // for debugging - - type LazyTree = AnyRef /* really: Tree | Lazy[Tree] */ - type LazyTreeList = AnyRef /* really: List[Tree] | Lazy[List[Tree]] */ - - /** Trees take a parameter indicating what the type of their `tpe` field - * is. Two choices: `Type` or `Untyped`. - * Untyped trees have type `Tree[Untyped]`. - * - * Tree typing uses a copy-on-write implementation: - * - * - You can never observe a `tpe` which is `null` (throws an exception) - * - So when creating a typed tree with `withType` we can re-use - * the existing tree transparently, assigning its `tpe` field, - * provided it was `null` before. - * - It is impossible to embed untyped trees in typed ones. - * - Typed trees can be embedded in untyped ones provided they are rooted - * in a TypedSplice node. - * - Type checking an untyped tree should remove all embedded `TypedSplice` - * nodes. - */ - abstract class Tree[-T >: Untyped] extends Positioned - with Product - with Attachment.Container - with printing.Showable - with Cloneable { - - if (Stats.enabled) ntrees += 1 - - private def nxId = { - nextId += 1 - //assert(nextId != 199, this) - nextId - } - - /** A unique identifier for this tree. Used for debugging, and potentially - * tracking presentation compiler interactions - */ - private var myUniqueId: Int = nxId - - def uniqueId = myUniqueId - - /** The type constructor at the root of the tree */ - type ThisTree[T >: Untyped] <: Tree[T] - - private[this] var myTpe: T = _ - - /** Destructively set the type of the tree. This should be called only when it is known that - * it is safe under sharing to do so. One use-case is in the withType method below - * which implements copy-on-write. Another use-case is in method interpolateAndAdapt in Typer, - * where we overwrite with a simplified version of the type itself. - */ - private[dotc] def overwriteType(tpe: T) = { - if (this.isInstanceOf[Template[_]]) assert(tpe.isInstanceOf[WithFixedSym], s"$this <--- $tpe") - myTpe = tpe - } - - /** The type of the tree. In case of an untyped tree, - * an UnAssignedTypeException is thrown. (Overridden by empty trees) - */ - def tpe: T @uncheckedVariance = { - if (myTpe == null) - throw new UnAssignedTypeException(this) - myTpe - } - - /** Copy `tpe` attribute from tree `from` into this tree, independently - * whether it is null or not. - final def copyAttr[U >: Untyped](from: Tree[U]): ThisTree[T] = { - val t1 = this.withPos(from.pos) - val t2 = - if (from.myTpe != null) t1.withType(from.myTpe.asInstanceOf[Type]) - else t1 - t2.asInstanceOf[ThisTree[T]] - } - */ - - /** Return a typed tree that's isomorphic to this tree, but has given - * type. (Overridden by empty trees) - */ - def withType(tpe: Type)(implicit ctx: Context): ThisTree[Type] = { - if (tpe == ErrorType) assert(ctx.reporter.errorsReported) - withTypeUnchecked(tpe) - } - - def withTypeUnchecked(tpe: Type): ThisTree[Type] = { - val tree = - (if (myTpe == null || - (myTpe.asInstanceOf[AnyRef] eq tpe.asInstanceOf[AnyRef])) this - else clone).asInstanceOf[Tree[Type]] - tree overwriteType tpe - tree.asInstanceOf[ThisTree[Type]] - } - - /** Does the tree have its type field set? Note: this operation is not - * referentially transparent, because it can observe the withType - * modifications. Should be used only in special circumstances (we - * need it for printing trees with optional type info). - */ - final def hasType: Boolean = myTpe != null - - final def typeOpt: Type = myTpe match { - case tp: Type => tp - case _ => NoType - } - - /** The denotation referred tno by this tree. - * Defined for `DenotingTree`s and `ProxyTree`s, NoDenotation for other - * kinds of trees - */ - def denot(implicit ctx: Context): Denotation = NoDenotation - - /** Shorthand for `denot.symbol`. */ - final def symbol(implicit ctx: Context): Symbol = denot.symbol - - /** Does this tree represent a type? */ - def isType: Boolean = false - - /** Does this tree represent a term? */ - def isTerm: Boolean = false - - /** Is this a legal part of a pattern which is not at the same time a term? */ - def isPattern: Boolean = false - - /** Does this tree define a new symbol that is not defined elsewhere? */ - def isDef: Boolean = false - - /** Is this tree either the empty tree or the empty ValDef or an empty type ident? */ - def isEmpty: Boolean = false - - /** Convert tree to a list. Gives a singleton list, except - * for thickets which return their element trees. - */ - def toList: List[Tree[T]] = this :: Nil - - /** if this tree is the empty tree, the alternative, else this tree */ - def orElse[U >: Untyped <: T](that: => Tree[U]): Tree[U] = - if (this eq genericEmptyTree) that else this - - /** The number of nodes in this tree */ - def treeSize: Int = { - var s = 1 - def addSize(elem: Any): Unit = elem match { - case t: Tree[_] => s += t.treeSize - case ts: List[_] => ts foreach addSize - case _ => - } - productIterator foreach addSize - s - } - - /** If this is a thicket, perform `op` on each of its trees - * otherwise, perform `op` ion tree itself. - */ - def foreachInThicket(op: Tree[T] => Unit): Unit = op(this) - - override def toText(printer: Printer) = printer.toText(this) - - override def hashCode(): Int = uniqueId // for debugging; was: System.identityHashCode(this) - override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] - - override def clone: Tree[T] = { - val tree = super.clone.asInstanceOf[Tree[T]] - tree.myUniqueId = nxId - tree - } - } - - class UnAssignedTypeException[T >: Untyped](tree: Tree[T]) extends RuntimeException { - override def getMessage: String = s"type of $tree is not assigned" - } - - // ------ Categories of trees ----------------------------------- - - /** Instances of this class are trees for which isType is definitely true. - * Note that some trees have isType = true without being TypTrees (e.g. Ident, AnnotatedTree) - */ - trait TypTree[-T >: Untyped] extends Tree[T] { - type ThisTree[-T >: Untyped] <: TypTree[T] - override def isType = true - } - - /** Instances of this class are trees for which isTerm is definitely true. - * Note that some trees have isTerm = true without being TermTrees (e.g. Ident, AnnotatedTree) - */ - trait TermTree[-T >: Untyped] extends Tree[T] { - type ThisTree[-T >: Untyped] <: TermTree[T] - override def isTerm = true - } - - /** Instances of this class are trees which are not terms but are legal - * parts of patterns. - */ - trait PatternTree[-T >: Untyped] extends Tree[T] { - type ThisTree[-T >: Untyped] <: PatternTree[T] - override def isPattern = true - } - - /** Tree's denotation can be derived from its type */ - abstract class DenotingTree[-T >: Untyped] extends Tree[T] { - type ThisTree[-T >: Untyped] <: DenotingTree[T] - override def denot(implicit ctx: Context) = tpe match { - case tpe: NamedType => tpe.denot - case tpe: ThisType => tpe.cls.denot - case tpe: AnnotatedType => tpe.stripAnnots match { - case tpe: NamedType => tpe.denot - case tpe: ThisType => tpe.cls.denot - case _ => NoDenotation - } - case _ => NoDenotation - } - } - - /** Tree's denot/isType/isTerm properties come from a subtree - * identified by `forwardTo`. - */ - abstract class ProxyTree[-T >: Untyped] extends Tree[T] { - type ThisTree[-T >: Untyped] <: ProxyTree[T] - def forwardTo: Tree[T] - override def denot(implicit ctx: Context): Denotation = forwardTo.denot - override def isTerm = forwardTo.isTerm - override def isType = forwardTo.isType - } - - /** Tree has a name */ - abstract class NameTree[-T >: Untyped] extends DenotingTree[T] { - type ThisTree[-T >: Untyped] <: NameTree[T] - def name: Name - } - - /** Tree refers by name to a denotation */ - abstract class RefTree[-T >: Untyped] extends NameTree[T] { - type ThisTree[-T >: Untyped] <: RefTree[T] - def qualifier: Tree[T] - override def isType = name.isTypeName - override def isTerm = name.isTermName - } - - /** Tree defines a new symbol */ - trait DefTree[-T >: Untyped] extends DenotingTree[T] { - type ThisTree[-T >: Untyped] <: DefTree[T] - override def isDef = true - def namedType = tpe.asInstanceOf[NamedType] - } - - /** Tree defines a new symbol and carries modifiers. - * The position of a MemberDef contains only the defined identifier or pattern. - * The envelope of a MemberDef contains the whole definition and has its point - * on the opening keyword (or the next token after that if keyword is missing). - */ - abstract class MemberDef[-T >: Untyped] extends NameTree[T] with DefTree[T] { - type ThisTree[-T >: Untyped] <: MemberDef[T] - - private[this] var myMods: untpd.Modifiers = null - - private[dotc] def rawMods: untpd.Modifiers = - if (myMods == null) untpd.EmptyModifiers else myMods - - def rawComment: Option[Comment] = getAttachment(DocComment) - - def withMods(mods: untpd.Modifiers): ThisTree[Untyped] = { - val tree = if (myMods == null || (myMods == mods)) this else clone.asInstanceOf[MemberDef[Untyped]] - tree.setMods(mods) - tree.asInstanceOf[ThisTree[Untyped]] - } - - def withFlags(flags: FlagSet): ThisTree[Untyped] = withMods(untpd.Modifiers(flags)) - - def setComment(comment: Option[Comment]): ThisTree[Untyped] = { - comment.map(putAttachment(DocComment, _)) - asInstanceOf[ThisTree[Untyped]] - } - - protected def setMods(mods: untpd.Modifiers) = myMods = mods - - /** The position of the name defined by this definition. - * This is a point position if the definition is synthetic, or a range position - * if the definition comes from source. - * It might also be that the definition does not have a position (for instance when synthesized by - * a calling chain from `viewExists`), in that case the return position is NoPosition. - */ - def namePos = - if (pos.exists) - if (rawMods.is(Synthetic)) Position(pos.point, pos.point) - else Position(pos.point, pos.point + name.length, pos.point) - else pos - } - - /** A ValDef or DefDef tree */ - trait ValOrDefDef[-T >: Untyped] extends MemberDef[T] with WithLazyField[Tree[T]] { - def tpt: Tree[T] - def unforcedRhs: LazyTree = unforced - def rhs(implicit ctx: Context): Tree[T] = forceIfLazy - } - - // ----------- Tree case classes ------------------------------------ - - /** name */ - case class Ident[-T >: Untyped] private[ast] (name: Name) - extends RefTree[T] { - type ThisTree[-T >: Untyped] = Ident[T] - def qualifier: Tree[T] = genericEmptyTree - } - - class BackquotedIdent[-T >: Untyped] private[ast] (name: Name) - extends Ident[T](name) { - override def toString = s"BackquotedIdent($name)" - } - - /** qualifier.name, or qualifier#name, if qualifier is a type */ - case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) - extends RefTree[T] { - type ThisTree[-T >: Untyped] = Select[T] - } - - class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature) - extends Select[T](qualifier, name) { - override def toString = s"SelectWithSig($qualifier, $name, $sig)" - } - - /** qual.this */ - case class This[-T >: Untyped] private[ast] (qual: untpd.Ident) - extends DenotingTree[T] with TermTree[T] { - type ThisTree[-T >: Untyped] = This[T] - // Denotation of a This tree is always the underlying class; needs correction for modules. - override def denot(implicit ctx: Context): Denotation = { - tpe match { - case tpe @ TermRef(pre, _) if tpe.symbol is Module => - tpe.symbol.moduleClass.denot.asSeenFrom(pre) - case _ => - super.denot - } - } - } - - /** C.super[mix], where qual = C.this */ - case class Super[-T >: Untyped] private[ast] (qual: Tree[T], mix: untpd.Ident) - extends ProxyTree[T] with TermTree[T] { - type ThisTree[-T >: Untyped] = Super[T] - def forwardTo = qual - } - - abstract class GenericApply[-T >: Untyped] extends ProxyTree[T] with TermTree[T] { - type ThisTree[-T >: Untyped] <: GenericApply[T] - val fun: Tree[T] - val args: List[Tree[T]] - def forwardTo = fun - } - - /** fun(args) */ - case class Apply[-T >: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]]) - extends GenericApply[T] { - type ThisTree[-T >: Untyped] = Apply[T] - } - - /** fun[args] */ - case class TypeApply[-T >: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]]) - extends GenericApply[T] { - type ThisTree[-T >: Untyped] = TypeApply[T] - } - - /** const */ - case class Literal[-T >: Untyped] private[ast] (const: Constant) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = Literal[T] - } - - /** new tpt, but no constructor call */ - case class New[-T >: Untyped] private[ast] (tpt: Tree[T]) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = New[T] - } - - /** expr : tpt */ - case class Typed[-T >: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T]) - extends ProxyTree[T] with TermTree[T] { - type ThisTree[-T >: Untyped] = Typed[T] - def forwardTo = expr - } - - /** name = arg, in a parameter list */ - case class NamedArg[-T >: Untyped] private[ast] (name: Name, arg: Tree[T]) - extends Tree[T] { - type ThisTree[-T >: Untyped] = NamedArg[T] - } - - /** name = arg, outside a parameter list */ - case class Assign[-T >: Untyped] private[ast] (lhs: Tree[T], rhs: Tree[T]) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = Assign[T] - } - - /** { stats; expr } */ - case class Block[-T >: Untyped] private[ast] (stats: List[Tree[T]], expr: Tree[T]) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = Block[T] - } - - /** if cond then thenp else elsep */ - case class If[-T >: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T]) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = If[T] - } - - /** A closure with an environment and a reference to a method. - * @param env The captured parameters of the closure - * @param meth A ref tree that refers to the method of the closure. - * The first (env.length) parameters of that method are filled - * with env values. - * @param tpt Either EmptyTree or a TypeTree. If tpt is EmptyTree the type - * of the closure is a function type, otherwise it is the type - * given in `tpt`, which must be a SAM type. - */ - case class Closure[-T >: Untyped] private[ast] (env: List[Tree[T]], meth: Tree[T], tpt: Tree[T]) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = Closure[T] - } - - /** selector match { cases } */ - case class Match[-T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]]) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = Match[T] - } - - /** case pat if guard => body; only appears as child of a Match */ - case class CaseDef[-T >: Untyped] private[ast] (pat: Tree[T], guard: Tree[T], body: Tree[T]) - extends Tree[T] { - type ThisTree[-T >: Untyped] = CaseDef[T] - } - - /** return expr - * where `from` refers to the method from which the return takes place - * After program transformations this is not necessarily the enclosing method, because - * closures can intervene. - */ - case class Return[-T >: Untyped] private[ast] (expr: Tree[T], from: Tree[T] = genericEmptyTree) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = Return[T] - } - - /** try block catch handler finally finalizer - * - * Note: if the handler is a case block CASES of the form - * - * { case1 ... caseN } - * - * the parser returns Match(EmptyTree, CASES). Desugaring and typing this yields a closure - * node - * - * { def $anonfun(x: Throwable) = x match CASES; Closure(Nil, $anonfun) } - * - * At some later stage when we normalize the try we can revert this to - * - * Match(EmptyTree, CASES) - * - * or else if stack is non-empty - * - * Match(EmptyTree, <case x: Throwable => $anonfun(x)>) - */ - case class Try[-T >: Untyped] private[ast] (expr: Tree[T], cases: List[CaseDef[T]], finalizer: Tree[T]) - extends TermTree[T] { - type ThisTree[-T >: Untyped] = Try[T] - } - - /** Seq(elems) - * @param tpt The element type of the sequence. - */ - case class SeqLiteral[-T >: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T]) - extends Tree[T] { - type ThisTree[-T >: Untyped] = SeqLiteral[T] - } - - /** Array(elems) */ - class JavaSeqLiteral[T >: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T]) - extends SeqLiteral(elems, elemtpt) { - override def toString = s"JavaSeqLiteral($elems, $elemtpt)" - } - - /** A tree representing inlined code. - * - * @param call Info about the original call that was inlined - * Until PostTyper, this is the full call, afterwards only - * a reference to the toplevel class from which the call was inlined. - * @param bindings Bindings for proxies to be used in the inlined code - * @param expansion The inlined tree, minus bindings. - * - * The full inlined code is equivalent to - * - * { bindings; expansion } - * - * The reason to keep `bindings` separate is because they are typed in a - * different context: `bindings` represent the arguments to the inlined - * call, whereas `expansion` represents the body of the inlined function. - */ - case class Inlined[-T >: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T]) - extends Tree[T] { - type ThisTree[-T >: Untyped] = Inlined[T] - } - - /** A type tree that represents an existing or inferred type */ - case class TypeTree[-T >: Untyped] () - extends DenotingTree[T] with TypTree[T] { - type ThisTree[-T >: Untyped] = TypeTree[T] - override def isEmpty = !hasType - override def toString = - s"TypeTree${if (hasType) s"[$typeOpt]" else ""}" - } - - /** ref.type */ - case class SingletonTypeTree[-T >: Untyped] private[ast] (ref: Tree[T]) - extends DenotingTree[T] with TypTree[T] { - type ThisTree[-T >: Untyped] = SingletonTypeTree[T] - } - - /** left & right */ - case class AndTypeTree[-T >: Untyped] private[ast] (left: Tree[T], right: Tree[T]) - extends TypTree[T] { - type ThisTree[-T >: Untyped] = AndTypeTree[T] - } - - /** left | right */ - case class OrTypeTree[-T >: Untyped] private[ast] (left: Tree[T], right: Tree[T]) - extends TypTree[T] { - type ThisTree[-T >: Untyped] = OrTypeTree[T] - } - - /** tpt { refinements } */ - case class RefinedTypeTree[-T >: Untyped] private[ast] (tpt: Tree[T], refinements: List[Tree[T]]) - extends ProxyTree[T] with TypTree[T] { - type ThisTree[-T >: Untyped] = RefinedTypeTree[T] - def forwardTo = tpt - } - - /** tpt[args] */ - case class AppliedTypeTree[-T >: Untyped] private[ast] (tpt: Tree[T], args: List[Tree[T]]) - extends ProxyTree[T] with TypTree[T] { - type ThisTree[-T >: Untyped] = AppliedTypeTree[T] - def forwardTo = tpt - } - - /** [typeparams] -> tpt */ - case class PolyTypeTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T]) - extends TypTree[T] { - type ThisTree[-T >: Untyped] = PolyTypeTree[T] - } - - /** => T */ - case class ByNameTypeTree[-T >: Untyped] private[ast] (result: Tree[T]) - extends TypTree[T] { - type ThisTree[-T >: Untyped] = ByNameTypeTree[T] - } - - /** >: lo <: hi */ - case class TypeBoundsTree[-T >: Untyped] private[ast] (lo: Tree[T], hi: Tree[T]) - extends TypTree[T] { - type ThisTree[-T >: Untyped] = TypeBoundsTree[T] - } - - /** name @ body */ - case class Bind[-T >: Untyped] private[ast] (name: Name, body: Tree[T]) - extends NameTree[T] with DefTree[T] with PatternTree[T] { - type ThisTree[-T >: Untyped] = Bind[T] - override def isType = name.isTypeName - override def isTerm = name.isTermName - } - - /** tree_1 | ... | tree_n */ - case class Alternative[-T >: Untyped] private[ast] (trees: List[Tree[T]]) - extends PatternTree[T] { - type ThisTree[-T >: Untyped] = Alternative[T] - } - - /** The typed translation of `extractor(patterns)` in a pattern. The translation has the following - * components: - * - * @param fun is `extractor.unapply` (or, for backwards compatibility, `extractor.unapplySeq`) - * possibly with type parameters - * @param implicits Any implicit parameters passed to the unapply after the selector - * @param patterns The argument patterns in the pattern match. - * - * It is typed with same type as first `fun` argument - * Given a match selector `sel` a pattern UnApply(fun, implicits, patterns) is roughly translated as follows - * - * val result = fun(sel)(implicits) - * if (result.isDefined) "match patterns against result" - */ - case class UnApply[-T >: Untyped] private[ast] (fun: Tree[T], implicits: List[Tree[T]], patterns: List[Tree[T]]) - extends PatternTree[T] { - type ThisTree[-T >: Untyped] = UnApply[T] - } - - /** mods val name: tpt = rhs */ - case class ValDef[-T >: Untyped] private[ast] (name: TermName, tpt: Tree[T], private var preRhs: LazyTree) - extends ValOrDefDef[T] { - type ThisTree[-T >: Untyped] = ValDef[T] - assert(isEmpty || tpt != genericEmptyTree) - def unforced = preRhs - protected def force(x: AnyRef) = preRhs = x - } - - /** mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs */ - case class DefDef[-T >: Untyped] private[ast] (name: TermName, tparams: List[TypeDef[T]], - vparamss: List[List[ValDef[T]]], tpt: Tree[T], private var preRhs: LazyTree) - extends ValOrDefDef[T] { - type ThisTree[-T >: Untyped] = DefDef[T] - assert(tpt != genericEmptyTree) - def unforced = preRhs - protected def force(x: AnyRef) = preRhs = x - } - - /** mods class name template or - * mods trait name template or - * mods type name = rhs or - * mods type name >: lo <: hi, if rhs = TypeBoundsTree(lo, hi) & (lo ne hi) - */ - case class TypeDef[-T >: Untyped] private[ast] (name: TypeName, rhs: Tree[T]) - extends MemberDef[T] { - type ThisTree[-T >: Untyped] = TypeDef[T] - - /** Is this a definition of a class? */ - def isClassDef = rhs.isInstanceOf[Template[_]] - } - - /** extends parents { self => body } */ - case class Template[-T >: Untyped] private[ast] (constr: DefDef[T], parents: List[Tree[T]], self: ValDef[T], private var preBody: LazyTreeList) - extends DefTree[T] with WithLazyField[List[Tree[T]]] { - type ThisTree[-T >: Untyped] = Template[T] - def unforcedBody = unforced - def unforced = preBody - protected def force(x: AnyRef) = preBody = x - def body(implicit ctx: Context): List[Tree[T]] = forceIfLazy - } - - /** import expr.selectors - * where a selector is either an untyped `Ident`, `name` or - * an untyped thicket consisting of `name` and `rename`. - */ - case class Import[-T >: Untyped] private[ast] (expr: Tree[T], selectors: List[Tree[Untyped]]) - extends DenotingTree[T] { - type ThisTree[-T >: Untyped] = Import[T] - } - - /** package pid { stats } */ - case class PackageDef[-T >: Untyped] private[ast] (pid: RefTree[T], stats: List[Tree[T]]) - extends ProxyTree[T] { - type ThisTree[-T >: Untyped] = PackageDef[T] - def forwardTo = pid - } - - /** arg @annot */ - case class Annotated[-T >: Untyped] private[ast] (arg: Tree[T], annot: Tree[T]) - extends ProxyTree[T] { - type ThisTree[-T >: Untyped] = Annotated[T] - def forwardTo = arg - } - - trait WithoutTypeOrPos[-T >: Untyped] extends Tree[T] { - override def tpe: T @uncheckedVariance = NoType.asInstanceOf[T] - override def withTypeUnchecked(tpe: Type) = this.asInstanceOf[ThisTree[Type]] - override def pos = NoPosition - override def setPos(pos: Position) = {} - } - - /** Temporary class that results from translation of ModuleDefs - * (and possibly other statements). - * The contained trees will be integrated when transformed with - * a `transform(List[Tree])` call. - */ - case class Thicket[-T >: Untyped](trees: List[Tree[T]]) - extends Tree[T] with WithoutTypeOrPos[T] { - type ThisTree[-T >: Untyped] = Thicket[T] - override def isEmpty: Boolean = trees.isEmpty - override def toList: List[Tree[T]] = flatten(trees) - override def toString = if (isEmpty) "EmptyTree" else "Thicket(" + trees.mkString(", ") + ")" - override def withPos(pos: Position): this.type = { - val newTrees = trees.map(_.withPos(pos)) - new Thicket[T](newTrees).asInstanceOf[this.type] - } - override def pos = (NoPosition /: trees) ((pos, t) => pos union t.pos) - override def foreachInThicket(op: Tree[T] => Unit): Unit = - trees foreach (_.foreachInThicket(op)) - } - - class EmptyValDef[T >: Untyped] extends ValDef[T]( - nme.WILDCARD, genericEmptyTree[T], genericEmptyTree[T]) with WithoutTypeOrPos[T] { - override def isEmpty: Boolean = true - setMods(untpd.Modifiers(PrivateLocal)) - } - - @sharable val theEmptyTree: Thicket[Type] = Thicket(Nil) - @sharable val theEmptyValDef = new EmptyValDef[Type] - - def genericEmptyValDef[T >: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]] - def genericEmptyTree[T >: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]] - - def flatten[T >: Untyped](trees: List[Tree[T]]): List[Tree[T]] = { - var buf: ListBuffer[Tree[T]] = null - var xs = trees - while (xs.nonEmpty) { - xs.head match { - case Thicket(elems) => - if (buf == null) { - buf = new ListBuffer - var ys = trees - while (ys ne xs) { - buf += ys.head - ys = ys.tail - } - } - for (elem <- elems) { - assert(!elem.isInstanceOf[Thicket[_]]) - buf += elem - } - case tree => - if (buf != null) buf += tree - } - xs = xs.tail - } - if (buf != null) buf.toList else trees - } - - // ----- Lazy trees and tree sequences - - /** A tree that can have a lazy field - * The field is represented by some private `var` which is - * proxied `unforced` and `force`. Forcing the field will - * set the `var` to the underlying value. - */ - trait WithLazyField[+T <: AnyRef] { - def unforced: AnyRef - protected def force(x: AnyRef): Unit - def forceIfLazy(implicit ctx: Context): T = unforced match { - case lzy: Lazy[T] => - val x = lzy.complete - force(x) - x - case x: T @ unchecked => x - } - } - - /** A base trait for lazy tree fields. - * These can be instantiated with Lazy instances which - * can delay tree construction until the field is first demanded. - */ - trait Lazy[T <: AnyRef] { - def complete(implicit ctx: Context): T - } - - // ----- Generic Tree Instances, inherited from `tpt` and `untpd`. - - abstract class Instance[T >: Untyped <: Type] extends DotClass { inst => - - type Tree = Trees.Tree[T] - type TypTree = Trees.TypTree[T] - type TermTree = Trees.TermTree[T] - type PatternTree = Trees.PatternTree[T] - type DenotingTree = Trees.DenotingTree[T] - type ProxyTree = Trees.ProxyTree[T] - type NameTree = Trees.NameTree[T] - type RefTree = Trees.RefTree[T] - type DefTree = Trees.DefTree[T] - type MemberDef = Trees.MemberDef[T] - type ValOrDefDef = Trees.ValOrDefDef[T] - - type Ident = Trees.Ident[T] - type BackquotedIdent = Trees.BackquotedIdent[T] - type Select = Trees.Select[T] - type SelectWithSig = Trees.SelectWithSig[T] - type This = Trees.This[T] - type Super = Trees.Super[T] - type Apply = Trees.Apply[T] - type TypeApply = Trees.TypeApply[T] - type Literal = Trees.Literal[T] - type New = Trees.New[T] - type Typed = Trees.Typed[T] - type NamedArg = Trees.NamedArg[T] - type Assign = Trees.Assign[T] - type Block = Trees.Block[T] - type If = Trees.If[T] - type Closure = Trees.Closure[T] - type Match = Trees.Match[T] - type CaseDef = Trees.CaseDef[T] - type Return = Trees.Return[T] - type Try = Trees.Try[T] - type SeqLiteral = Trees.SeqLiteral[T] - type JavaSeqLiteral = Trees.JavaSeqLiteral[T] - type Inlined = Trees.Inlined[T] - type TypeTree = Trees.TypeTree[T] - type SingletonTypeTree = Trees.SingletonTypeTree[T] - type AndTypeTree = Trees.AndTypeTree[T] - type OrTypeTree = Trees.OrTypeTree[T] - type RefinedTypeTree = Trees.RefinedTypeTree[T] - type AppliedTypeTree = Trees.AppliedTypeTree[T] - type PolyTypeTree = Trees.PolyTypeTree[T] - type ByNameTypeTree = Trees.ByNameTypeTree[T] - type TypeBoundsTree = Trees.TypeBoundsTree[T] - type Bind = Trees.Bind[T] - type Alternative = Trees.Alternative[T] - type UnApply = Trees.UnApply[T] - type ValDef = Trees.ValDef[T] - type DefDef = Trees.DefDef[T] - type TypeDef = Trees.TypeDef[T] - type Template = Trees.Template[T] - type Import = Trees.Import[T] - type PackageDef = Trees.PackageDef[T] - type Annotated = Trees.Annotated[T] - type Thicket = Trees.Thicket[T] - - @sharable val EmptyTree: Thicket = genericEmptyTree - @sharable val EmptyValDef: ValDef = genericEmptyValDef - - // ----- Auxiliary creation methods ------------------ - - def Thicket(trees: List[Tree]): Thicket = new Thicket(trees) - def Thicket(): Thicket = EmptyTree - def Thicket(x1: Tree, x2: Tree): Thicket = Thicket(x1 :: x2 :: Nil) - def Thicket(x1: Tree, x2: Tree, x3: Tree): Thicket = Thicket(x1 :: x2 :: x3 :: Nil) - def flatTree(xs: List[Tree]): Tree = flatten(xs) match { - case x :: Nil => x - case ys => Thicket(ys) - } - - // ----- Helper classes for copying, transforming, accumulating ----------------- - - val cpy: TreeCopier - - /** A class for copying trees. The copy methods avoid creating a new tree - * If all arguments stay the same. - * - * Note: Some of the copy methods take a context. - * These are exactly those methods that are overridden in TypedTreeCopier - * so that they selectively retype themselves. Retyping needs a context. - */ - abstract class TreeCopier { - - def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[T] - def postProcess(tree: Tree, copied: untpd.MemberDef): copied.ThisTree[T] - - def finalize(tree: Tree, copied: untpd.Tree): copied.ThisTree[T] = - postProcess(tree, copied withPos tree.pos) - - def finalize(tree: Tree, copied: untpd.MemberDef): copied.ThisTree[T] = - postProcess(tree, copied withPos tree.pos) - - def Ident(tree: Tree)(name: Name): Ident = tree match { - case tree: BackquotedIdent => - if (name == tree.name) tree - else finalize(tree, new BackquotedIdent(name)) - case tree: Ident if name == tree.name => tree - case _ => finalize(tree, untpd.Ident(name)) - } - def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = tree match { - case tree: SelectWithSig => - if ((qualifier eq tree.qualifier) && (name == tree.name)) tree - else finalize(tree, new SelectWithSig(qualifier, name, tree.sig)) - case tree: Select if (qualifier eq tree.qualifier) && (name == tree.name) => tree - case _ => finalize(tree, untpd.Select(qualifier, name)) - } - def This(tree: Tree)(qual: untpd.Ident): This = tree match { - case tree: This if qual eq tree.qual => tree - case _ => finalize(tree, untpd.This(qual)) - } - def Super(tree: Tree)(qual: Tree, mix: untpd.Ident): Super = tree match { - case tree: Super if (qual eq tree.qual) && (mix eq tree.mix) => tree - case _ => finalize(tree, untpd.Super(qual, mix)) - } - def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = tree match { - case tree: Apply if (fun eq tree.fun) && (args eq tree.args) => tree - case _ => finalize(tree, untpd.Apply(fun, args)) - } - def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = tree match { - case tree: TypeApply if (fun eq tree.fun) && (args eq tree.args) => tree - case _ => finalize(tree, untpd.TypeApply(fun, args)) - } - def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal = tree match { - case tree: Literal if const == tree.const => tree - case _ => finalize(tree, untpd.Literal(const)) - } - def New(tree: Tree)(tpt: Tree)(implicit ctx: Context): New = tree match { - case tree: New if tpt eq tree.tpt => tree - case _ => finalize(tree, untpd.New(tpt)) - } - def Typed(tree: Tree)(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = tree match { - case tree: Typed if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.Typed(expr, tpt)) - } - def NamedArg(tree: Tree)(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = tree match { - case tree: NamedArg if (name == tree.name) && (arg eq tree.arg) => tree - case _ => finalize(tree, untpd.NamedArg(name, arg)) - } - def Assign(tree: Tree)(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = tree match { - case tree: Assign if (lhs eq tree.lhs) && (rhs eq tree.rhs) => tree - case _ => finalize(tree, untpd.Assign(lhs, rhs)) - } - def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = tree match { - case tree: Block if (stats eq tree.stats) && (expr eq tree.expr) => tree - case _ => finalize(tree, untpd.Block(stats, expr)) - } - def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = tree match { - case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree - case _ => finalize(tree, untpd.If(cond, thenp, elsep)) - } - def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = tree match { - case tree: Closure if (env eq tree.env) && (meth eq tree.meth) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.Closure(env, meth, tpt)) - } - def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = tree match { - case tree: Match if (selector eq tree.selector) && (cases eq tree.cases) => tree - case _ => finalize(tree, untpd.Match(selector, cases)) - } - def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = tree match { - case tree: CaseDef if (pat eq tree.pat) && (guard eq tree.guard) && (body eq tree.body) => tree - case _ => finalize(tree, untpd.CaseDef(pat, guard, body)) - } - def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return = tree match { - case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree - case _ => finalize(tree, untpd.Return(expr, from)) - } - def Try(tree: Tree)(expr: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try = tree match { - case tree: Try if (expr eq tree.expr) && (cases eq tree.cases) && (finalizer eq tree.finalizer) => tree - case _ => finalize(tree, untpd.Try(expr, cases, finalizer)) - } - def SeqLiteral(tree: Tree)(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = tree match { - case tree: JavaSeqLiteral => - if ((elems eq tree.elems) && (elemtpt eq tree.elemtpt)) tree - else finalize(tree, new JavaSeqLiteral(elems, elemtpt)) - case tree: SeqLiteral if (elems eq tree.elems) && (elemtpt eq tree.elemtpt) => tree - case _ => finalize(tree, untpd.SeqLiteral(elems, elemtpt)) - } - def Inlined(tree: Tree)(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit ctx: Context): Inlined = tree match { - case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree - case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)) - } - def SingletonTypeTree(tree: Tree)(ref: Tree): SingletonTypeTree = tree match { - case tree: SingletonTypeTree if ref eq tree.ref => tree - case _ => finalize(tree, untpd.SingletonTypeTree(ref)) - } - def AndTypeTree(tree: Tree)(left: Tree, right: Tree): AndTypeTree = tree match { - case tree: AndTypeTree if (left eq tree.left) && (right eq tree.right) => tree - case _ => finalize(tree, untpd.AndTypeTree(left, right)) - } - def OrTypeTree(tree: Tree)(left: Tree, right: Tree): OrTypeTree = tree match { - case tree: OrTypeTree if (left eq tree.left) && (right eq tree.right) => tree - case _ => finalize(tree, untpd.OrTypeTree(left, right)) - } - def RefinedTypeTree(tree: Tree)(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = tree match { - case tree: RefinedTypeTree if (tpt eq tree.tpt) && (refinements eq tree.refinements) => tree - case _ => finalize(tree, untpd.RefinedTypeTree(tpt, refinements)) - } - def AppliedTypeTree(tree: Tree)(tpt: Tree, args: List[Tree]): AppliedTypeTree = tree match { - case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree - case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args)) - } - def PolyTypeTree(tree: Tree)(tparams: List[TypeDef], body: Tree): PolyTypeTree = tree match { - case tree: PolyTypeTree if (tparams eq tree.tparams) && (body eq tree.body) => tree - case _ => finalize(tree, untpd.PolyTypeTree(tparams, body)) - } - def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match { - case tree: ByNameTypeTree if result eq tree.result => tree - case _ => finalize(tree, untpd.ByNameTypeTree(result)) - } - def TypeBoundsTree(tree: Tree)(lo: Tree, hi: Tree): TypeBoundsTree = tree match { - case tree: TypeBoundsTree if (lo eq tree.lo) && (hi eq tree.hi) => tree - case _ => finalize(tree, untpd.TypeBoundsTree(lo, hi)) - } - def Bind(tree: Tree)(name: Name, body: Tree): Bind = tree match { - case tree: Bind if (name eq tree.name) && (body eq tree.body) => tree - case _ => finalize(tree, untpd.Bind(name, body)) - } - def Alternative(tree: Tree)(trees: List[Tree]): Alternative = tree match { - case tree: Alternative if trees eq tree.trees => tree - case _ => finalize(tree, untpd.Alternative(trees)) - } - def UnApply(tree: Tree)(fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = tree match { - case tree: UnApply if (fun eq tree.fun) && (implicits eq tree.implicits) && (patterns eq tree.patterns) => tree - case _ => finalize(tree, untpd.UnApply(fun, implicits, patterns)) - } - def ValDef(tree: Tree)(name: TermName, tpt: Tree, rhs: LazyTree): ValDef = tree match { - case tree: ValDef if (name == tree.name) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree - case _ => finalize(tree, untpd.ValDef(name, tpt, rhs)) - } - def DefDef(tree: Tree)(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree): DefDef = tree match { - case tree: DefDef if (name == tree.name) && (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree - case _ => finalize(tree, untpd.DefDef(name, tparams, vparamss, tpt, rhs)) - } - def TypeDef(tree: Tree)(name: TypeName, rhs: Tree): TypeDef = tree match { - case tree: TypeDef if (name == tree.name) && (rhs eq tree.rhs) => tree - case _ => finalize(tree, untpd.TypeDef(name, rhs)) - } - def Template(tree: Tree)(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList): Template = tree match { - case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (self eq tree.self) && (body eq tree.unforcedBody) => tree - case _ => finalize(tree, untpd.Template(constr, parents, self, body)) - } - def Import(tree: Tree)(expr: Tree, selectors: List[untpd.Tree]): Import = tree match { - case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree - case _ => finalize(tree, untpd.Import(expr, selectors)) - } - def PackageDef(tree: Tree)(pid: RefTree, stats: List[Tree]): PackageDef = tree match { - case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree - case _ => finalize(tree, untpd.PackageDef(pid, stats)) - } - def Annotated(tree: Tree)(arg: Tree, annot: Tree)(implicit ctx: Context): Annotated = tree match { - case tree: Annotated if (arg eq tree.arg) && (annot eq tree.annot) => tree - case _ => finalize(tree, untpd.Annotated(arg, annot)) - } - def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match { - case tree: Thicket if trees eq tree.trees => tree - case _ => finalize(tree, untpd.Thicket(trees)) - } - - // Copier methods with default arguments; these demand that the original tree - // is of the same class as the copy. We only include trees with more than 2 elements here. - def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If = - If(tree: Tree)(cond, thenp, elsep) - def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure = - Closure(tree: Tree)(env, meth, tpt) - def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef = - CaseDef(tree: Tree)(pat, guard, body) - def Try(tree: Try)(expr: Tree = tree.expr, cases: List[CaseDef] = tree.cases, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try = - Try(tree: Tree)(expr, cases, finalizer) - def UnApply(tree: UnApply)(fun: Tree = tree.fun, implicits: List[Tree] = tree.implicits, patterns: List[Tree] = tree.patterns): UnApply = - UnApply(tree: Tree)(fun, implicits, patterns) - def ValDef(tree: ValDef)(name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs): ValDef = - ValDef(tree: Tree)(name, tpt, rhs) - def DefDef(tree: DefDef)(name: TermName = tree.name, tparams: List[TypeDef] = tree.tparams, vparamss: List[List[ValDef]] = tree.vparamss, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs): DefDef = - DefDef(tree: Tree)(name, tparams, vparamss, tpt, rhs) - def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs): TypeDef = - TypeDef(tree: Tree)(name, rhs) - def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = - Template(tree: Tree)(constr, parents, self, body) - } - - abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { - - def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Ident(name) => - tree - case Select(qualifier, name) => - cpy.Select(tree)(transform(qualifier), name) - case This(qual) => - tree - case Super(qual, mix) => - cpy.Super(tree)(transform(qual), mix) - case Apply(fun, args) => - cpy.Apply(tree)(transform(fun), transform(args)) - case TypeApply(fun, args) => - cpy.TypeApply(tree)(transform(fun), transform(args)) - case Literal(const) => - tree - case New(tpt) => - cpy.New(tree)(transform(tpt)) - case Typed(expr, tpt) => - cpy.Typed(tree)(transform(expr), transform(tpt)) - case NamedArg(name, arg) => - cpy.NamedArg(tree)(name, transform(arg)) - case Assign(lhs, rhs) => - cpy.Assign(tree)(transform(lhs), transform(rhs)) - case Block(stats, expr) => - cpy.Block(tree)(transformStats(stats), transform(expr)) - case If(cond, thenp, elsep) => - cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) - case Closure(env, meth, tpt) => - cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) - case Match(selector, cases) => - cpy.Match(tree)(transform(selector), transformSub(cases)) - case CaseDef(pat, guard, body) => - cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) - case Return(expr, from) => - cpy.Return(tree)(transform(expr), transformSub(from)) - case Try(block, cases, finalizer) => - cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer)) - case SeqLiteral(elems, elemtpt) => - cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) - case Inlined(call, bindings, expansion) => - cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)) - case TypeTree() => - tree - case SingletonTypeTree(ref) => - cpy.SingletonTypeTree(tree)(transform(ref)) - case AndTypeTree(left, right) => - cpy.AndTypeTree(tree)(transform(left), transform(right)) - case OrTypeTree(left, right) => - cpy.OrTypeTree(tree)(transform(left), transform(right)) - case RefinedTypeTree(tpt, refinements) => - cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) - case AppliedTypeTree(tpt, args) => - cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) - case PolyTypeTree(tparams, body) => - cpy.PolyTypeTree(tree)(transformSub(tparams), transform(body)) - case ByNameTypeTree(result) => - cpy.ByNameTypeTree(tree)(transform(result)) - case TypeBoundsTree(lo, hi) => - cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) - case Bind(name, body) => - cpy.Bind(tree)(name, transform(body)) - case Alternative(trees) => - cpy.Alternative(tree)(transform(trees)) - case UnApply(fun, implicits, patterns) => - cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) - case EmptyValDef => - tree - case tree @ ValDef(name, tpt, _) => - val tpt1 = transform(tpt) - val rhs1 = transform(tree.rhs) - cpy.ValDef(tree)(name, tpt1, rhs1) - case tree @ DefDef(name, tparams, vparamss, tpt, _) => - cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) - case tree @ TypeDef(name, rhs) => - cpy.TypeDef(tree)(name, transform(rhs)) - case tree @ Template(constr, parents, self, _) => - cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(tree.body)) - case Import(expr, selectors) => - cpy.Import(tree)(transform(expr), selectors) - case PackageDef(pid, stats) => - cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)) - case Annotated(arg, annot) => - cpy.Annotated(tree)(transform(arg), transform(annot)) - case Thicket(trees) => - val trees1 = transform(trees) - if (trees1 eq trees) tree else Thicket(trees1) - } - - def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] = - transform(trees) - def transform(trees: List[Tree])(implicit ctx: Context): List[Tree] = - flatten(trees mapConserve (transform(_))) - def transformSub[Tr <: Tree](tree: Tr)(implicit ctx: Context): Tr = - transform(tree).asInstanceOf[Tr] - def transformSub[Tr <: Tree](trees: List[Tr])(implicit ctx: Context): List[Tr] = - transform(trees).asInstanceOf[List[Tr]] - } - - abstract class TreeAccumulator[X] { - def apply(x: X, tree: Tree)(implicit ctx: Context): X - def apply(x: X, trees: Traversable[Tree])(implicit ctx: Context): X = (x /: trees)(apply) - def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = { - def localCtx = - if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx - tree match { - case Ident(name) => - x - case Select(qualifier, name) => - this(x, qualifier) - case This(qual) => - x - case Super(qual, mix) => - this(x, qual) - case Apply(fun, args) => - this(this(x, fun), args) - case TypeApply(fun, args) => - this(this(x, fun), args) - case Literal(const) => - x - case New(tpt) => - this(x, tpt) - case Typed(expr, tpt) => - this(this(x, expr), tpt) - case NamedArg(name, arg) => - this(x, arg) - case Assign(lhs, rhs) => - this(this(x, lhs), rhs) - case Block(stats, expr) => - this(this(x, stats), expr) - case If(cond, thenp, elsep) => - this(this(this(x, cond), thenp), elsep) - case Closure(env, meth, tpt) => - this(this(this(x, env), meth), tpt) - case Match(selector, cases) => - this(this(x, selector), cases) - case CaseDef(pat, guard, body) => - this(this(this(x, pat), guard), body) - case Return(expr, from) => - this(this(x, expr), from) - case Try(block, handler, finalizer) => - this(this(this(x, block), handler), finalizer) - case SeqLiteral(elems, elemtpt) => - this(this(x, elems), elemtpt) - case Inlined(call, bindings, expansion) => - this(this(x, bindings), expansion) - case TypeTree() => - x - case SingletonTypeTree(ref) => - this(x, ref) - case AndTypeTree(left, right) => - this(this(x, left), right) - case OrTypeTree(left, right) => - this(this(x, left), right) - case RefinedTypeTree(tpt, refinements) => - this(this(x, tpt), refinements) - case AppliedTypeTree(tpt, args) => - this(this(x, tpt), args) - case PolyTypeTree(tparams, body) => - implicit val ctx: Context = localCtx - this(this(x, tparams), body) - case ByNameTypeTree(result) => - this(x, result) - case TypeBoundsTree(lo, hi) => - this(this(x, lo), hi) - case Bind(name, body) => - this(x, body) - case Alternative(trees) => - this(x, trees) - case UnApply(fun, implicits, patterns) => - this(this(this(x, fun), implicits), patterns) - case tree @ ValDef(name, tpt, _) => - implicit val ctx: Context = localCtx - this(this(x, tpt), tree.rhs) - case tree @ DefDef(name, tparams, vparamss, tpt, _) => - implicit val ctx: Context = localCtx - this(this((this(x, tparams) /: vparamss)(apply), tpt), tree.rhs) - case TypeDef(name, rhs) => - implicit val ctx: Context = localCtx - this(x, rhs) - case tree @ Template(constr, parents, self, _) => - this(this(this(this(x, constr), parents), self), tree.body) - case Import(expr, selectors) => - this(x, expr) - case PackageDef(pid, stats) => - this(this(x, pid), stats)(localCtx) - case Annotated(arg, annot) => - this(this(x, arg), annot) - case Thicket(ts) => - this(x, ts) - } - } - } - - abstract class TreeTraverser extends TreeAccumulator[Unit] { - def traverse(tree: Tree)(implicit ctx: Context): Unit - def apply(x: Unit, tree: Tree)(implicit ctx: Context) = traverse(tree) - protected def traverseChildren(tree: Tree)(implicit ctx: Context) = foldOver((), tree) - } - - /** Fold `f` over all tree nodes, in depth-first, prefix order */ - class DeepFolder[X](f: (X, Tree) => X) extends TreeAccumulator[X] { - def apply(x: X, tree: Tree)(implicit ctx: Context): X = foldOver(f(x, tree), tree) - } - - /** Fold `f` over all tree nodes, in depth-first, prefix order, but don't visit - * subtrees where `f` returns a different result for the root, i.e. `f(x, root) ne x`. - */ - class ShallowFolder[X](f: (X, Tree) => X) extends TreeAccumulator[X] { - def apply(x: X, tree: Tree)(implicit ctx: Context): X = { - val x1 = f(x, tree) - if (x1.asInstanceOf[AnyRef] ne x1.asInstanceOf[AnyRef]) x1 - else foldOver(x1, tree) - } - } - - def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[T] = { - tree match { - case tree: Ident => cpy.Ident(tree)(newName) - case tree: Select => cpy.Select(tree)(tree.qualifier, newName) - case tree: Bind => cpy.Bind(tree)(newName, tree.body) - case tree: ValDef => cpy.ValDef(tree)(name = newName.asTermName) - case tree: DefDef => cpy.DefDef(tree)(name = newName.asTermName) - case tree: TypeDef => cpy.TypeDef(tree)(name = newName.asTypeName) - } - }.asInstanceOf[tree.ThisTree[T]] - } -} diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala deleted file mode 100644 index 44e1cf188..000000000 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ /dev/null @@ -1,952 +0,0 @@ -package dotty.tools -package dotc -package ast - -import dotty.tools.dotc.transform.{ExplicitOuter, Erasure} -import dotty.tools.dotc.typer.ProtoTypes.FunProtoTyped -import transform.SymUtils._ -import core._ -import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ -import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._ -import Denotations._, Decorators._, DenotTransformers._ -import collection.mutable -import util.{Property, SourceFile, NoSource} -import typer.ErrorReporting._ - -import scala.annotation.tailrec -import scala.io.Codec - -/** Some creators for typed trees */ -object tpd extends Trees.Instance[Type] with TypedTreeInfo { - - private def ta(implicit ctx: Context) = ctx.typeAssigner - - def Ident(tp: NamedType)(implicit ctx: Context): Ident = - ta.assignType(untpd.Ident(tp.name), tp) - - def Select(qualifier: Tree, name: Name)(implicit ctx: Context): Select = - ta.assignType(untpd.Select(qualifier, name), qualifier) - - def Select(qualifier: Tree, tp: NamedType)(implicit ctx: Context): Select = - untpd.Select(qualifier, tp.name).withType(tp) - - def This(cls: ClassSymbol)(implicit ctx: Context): This = - untpd.This(untpd.Ident(cls.name)).withType(cls.thisType) - - def Super(qual: Tree, mix: untpd.Ident, inConstrCall: Boolean, mixinClass: Symbol)(implicit ctx: Context): Super = - ta.assignType(untpd.Super(qual, mix), qual, inConstrCall, mixinClass) - - def Super(qual: Tree, mixName: TypeName, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context): Super = - Super(qual, if (mixName.isEmpty) untpd.EmptyTypeIdent else untpd.Ident(mixName), inConstrCall, mixinClass) - - def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = - ta.assignType(untpd.Apply(fn, args), fn, args) - - def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = - ta.assignType(untpd.TypeApply(fn, args), fn, args) - - def Literal(const: Constant)(implicit ctx: Context): Literal = - ta.assignType(untpd.Literal(const)) - - def unitLiteral(implicit ctx: Context): Literal = - Literal(Constant(())) - - def New(tpt: Tree)(implicit ctx: Context): New = - ta.assignType(untpd.New(tpt), tpt) - - def New(tp: Type)(implicit ctx: Context): New = New(TypeTree(tp)) - - def Typed(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = - ta.assignType(untpd.Typed(expr, tpt), tpt) - - def NamedArg(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = - ta.assignType(untpd.NamedArg(name, arg), arg) - - def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = - ta.assignType(untpd.Assign(lhs, rhs)) - - def Block(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = - ta.assignType(untpd.Block(stats, expr), stats, expr) - - /** Join `stats` in front of `expr` creating a new block if necessary */ - def seq(stats: List[Tree], expr: Tree)(implicit ctx: Context): Tree = - if (stats.isEmpty) expr - else expr match { - case Block(estats, eexpr) => cpy.Block(expr)(stats ::: estats, eexpr) - case _ => Block(stats, expr) - } - - def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = - ta.assignType(untpd.If(cond, thenp, elsep), thenp, elsep) - - def Closure(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = - ta.assignType(untpd.Closure(env, meth, tpt), meth, tpt) - - /** A function def - * - * vparams => expr - * - * gets expanded to - * - * { def $anonfun(vparams) = expr; Closure($anonfun) } - * - * where the closure's type is the target type of the expression (FunctionN, unless - * otherwise specified). - */ - def Closure(meth: TermSymbol, rhsFn: List[List[Tree]] => Tree, targs: List[Tree] = Nil, targetType: Type = NoType)(implicit ctx: Context): Block = { - val targetTpt = if (targetType.exists) TypeTree(targetType) else EmptyTree - val call = - if (targs.isEmpty) Ident(TermRef(NoPrefix, meth)) - else TypeApply(Ident(TermRef(NoPrefix, meth)), targs) - Block( - DefDef(meth, rhsFn) :: Nil, - Closure(Nil, call, targetTpt)) - } - - def CaseDef(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = - ta.assignType(untpd.CaseDef(pat, guard, body), body) - - def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = - ta.assignType(untpd.Match(selector, cases), cases) - - def Return(expr: Tree, from: Tree)(implicit ctx: Context): Return = - ta.assignType(untpd.Return(expr, from)) - - def Try(block: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try = - ta.assignType(untpd.Try(block, cases, finalizer), block, cases) - - def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = - ta.assignType(untpd.SeqLiteral(elems, elemtpt), elems, elemtpt) - - def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): JavaSeqLiteral = - ta.assignType(new untpd.JavaSeqLiteral(elems, elemtpt), elems, elemtpt).asInstanceOf[JavaSeqLiteral] - - def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(implicit ctx: Context): Inlined = - ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - - def TypeTree(tp: Type)(implicit ctx: Context): TypeTree = - untpd.TypeTree().withType(tp) - - def SingletonTypeTree(ref: Tree)(implicit ctx: Context): SingletonTypeTree = - ta.assignType(untpd.SingletonTypeTree(ref), ref) - - def AndTypeTree(left: Tree, right: Tree)(implicit ctx: Context): AndTypeTree = - ta.assignType(untpd.AndTypeTree(left, right), left, right) - - def OrTypeTree(left: Tree, right: Tree)(implicit ctx: Context): OrTypeTree = - ta.assignType(untpd.OrTypeTree(left, right), left, right) - - def RefinedTypeTree(parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context): Tree = - ta.assignType(untpd.RefinedTypeTree(parent, refinements), parent, refinements, refineCls) - - def AppliedTypeTree(tycon: Tree, args: List[Tree])(implicit ctx: Context): AppliedTypeTree = - ta.assignType(untpd.AppliedTypeTree(tycon, args), tycon, args) - - def ByNameTypeTree(result: Tree)(implicit ctx: Context): ByNameTypeTree = - ta.assignType(untpd.ByNameTypeTree(result), result) - - def PolyTypeTree(tparams: List[TypeDef], body: Tree)(implicit ctx: Context): PolyTypeTree = - ta.assignType(untpd.PolyTypeTree(tparams, body), tparams, body) - - def TypeBoundsTree(lo: Tree, hi: Tree)(implicit ctx: Context): TypeBoundsTree = - ta.assignType(untpd.TypeBoundsTree(lo, hi), lo, hi) - - def Bind(sym: TermSymbol, body: Tree)(implicit ctx: Context): Bind = - ta.assignType(untpd.Bind(sym.name, body), sym) - - /** A pattern corresponding to `sym: tpe` */ - def BindTyped(sym: TermSymbol, tpe: Type)(implicit ctx: Context): Bind = - Bind(sym, Typed(Underscore(tpe), TypeTree(tpe))) - - def Alternative(trees: List[Tree])(implicit ctx: Context): Alternative = - ta.assignType(untpd.Alternative(trees), trees) - - def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree], proto: Type)(implicit ctx: Context): UnApply = - ta.assignType(untpd.UnApply(fun, implicits, patterns), proto) - - def ValDef(sym: TermSymbol, rhs: LazyTree = EmptyTree)(implicit ctx: Context): ValDef = - ta.assignType(untpd.ValDef(sym.name, TypeTree(sym.info), rhs), sym) - - def SyntheticValDef(name: TermName, rhs: Tree)(implicit ctx: Context): ValDef = - ValDef(ctx.newSymbol(ctx.owner, name, Synthetic, rhs.tpe.widen, coord = rhs.pos), rhs) - - def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = - ta.assignType(DefDef(sym, Function.const(rhs) _), sym) - - def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = - polyDefDef(sym, Function.const(rhsFn)) - - def polyDefDef(sym: TermSymbol, rhsFn: List[Type] => List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = { - val (tparams, mtp) = sym.info match { - case tp: PolyType => - val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateBounds) - (tparams, tp.instantiate(tparams map (_.typeRef))) - case tp => (Nil, tp) - } - - def valueParamss(tp: Type): (List[List[TermSymbol]], Type) = tp match { - case tp @ MethodType(paramNames, paramTypes) => - def valueParam(name: TermName, info: Type): TermSymbol = { - val maybeImplicit = if (tp.isInstanceOf[ImplicitMethodType]) Implicit else EmptyFlags - ctx.newSymbol(sym, name, TermParam | maybeImplicit, info) - } - val params = (paramNames, paramTypes).zipped.map(valueParam) - val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef))) - (params :: paramss, rtp) - case tp => (Nil, tp.widenExpr) - } - val (vparamss, rtp) = valueParamss(mtp) - val targs = tparams map (_.typeRef) - val argss = vparamss.nestedMap(vparam => Ident(vparam.termRef)) - ta.assignType( - untpd.DefDef( - sym.name, - tparams map TypeDef, - vparamss.nestedMap(ValDef(_)), - TypeTree(rtp), - rhsFn(targs)(argss)), - sym) - } - - def TypeDef(sym: TypeSymbol)(implicit ctx: Context): TypeDef = - ta.assignType(untpd.TypeDef(sym.name, TypeTree(sym.info)), sym) - - def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree], superArgs: List[Tree] = Nil)(implicit ctx: Context): TypeDef = { - val firstParentRef :: otherParentRefs = cls.info.parents - val firstParent = cls.typeRef.baseTypeWithArgs(firstParentRef.symbol) - val superRef = - if (cls is Trait) TypeTree(firstParent) - else { - def isApplicable(ctpe: Type): Boolean = ctpe match { - case ctpe: PolyType => - isApplicable(ctpe.instantiate(firstParent.argTypes)) - case ctpe: MethodType => - (superArgs corresponds ctpe.paramTypes)(_.tpe <:< _) - case _ => - false - } - val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(constr => isApplicable(constr.info)) - New(firstParent, constr.symbol.asTerm, superArgs) - } - val parents = superRef :: otherParentRefs.map(TypeTree(_)) - - val selfType = - if (cls.classInfo.selfInfo ne NoType) ValDef(ctx.newSelfSym(cls)) - else EmptyValDef - def isOwnTypeParam(stat: Tree) = - (stat.symbol is TypeParam) && stat.symbol.owner == cls - val bodyTypeParams = body filter isOwnTypeParam map (_.symbol) - val newTypeParams = - for (tparam <- cls.typeParams if !(bodyTypeParams contains tparam)) - yield TypeDef(tparam) - val findLocalDummy = new FindLocalDummyAccumulator(cls) - val localDummy = ((NoSymbol: Symbol) /: body)(findLocalDummy.apply) - .orElse(ctx.newLocalDummy(cls)) - val impl = untpd.Template(constr, parents, selfType, newTypeParams ++ body) - .withType(localDummy.nonMemberTermRef) - ta.assignType(untpd.TypeDef(cls.name, impl), cls) - } - - /** An anonymous class - * - * new parents { forwarders } - * - * where `forwarders` contains forwarders for all functions in `fns`. - * @param parents a non-empty list of class types - * @param fns a non-empty of functions for which forwarders should be defined in the class. - * The class has the same owner as the first function in `fns`. - * Its position is the union of all functions in `fns`. - */ - def AnonClass(parents: List[Type], fns: List[TermSymbol], methNames: List[TermName])(implicit ctx: Context): Block = { - val owner = fns.head.owner - val parents1 = - if (parents.head.classSymbol.is(Trait)) defn.ObjectType :: parents - else parents - val cls = ctx.newNormalizedClassSymbol(owner, tpnme.ANON_FUN, Synthetic, parents1, - coord = fns.map(_.pos).reduceLeft(_ union _)) - val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil).entered - def forwarder(fn: TermSymbol, name: TermName) = { - val fwdMeth = fn.copy(cls, name, Synthetic | Method).entered.asTerm - DefDef(fwdMeth, prefss => ref(fn).appliedToArgss(prefss)) - } - val forwarders = (fns, methNames).zipped.map(forwarder) - val cdef = ClassDef(cls, DefDef(constr), forwarders) - Block(cdef :: Nil, New(cls.typeRef, Nil)) - } - - // { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() } - def WhileDo(owner: Symbol, cond: Tree, body: List[Tree])(implicit ctx: Context): Tree = { - val sym = ctx.newSymbol(owner, nme.WHILE_PREFIX, Flags.Label | Flags.Synthetic, - MethodType(Nil, defn.UnitType), coord = cond.pos) - - val call = Apply(ref(sym), Nil) - val rhs = If(cond, Block(body, call), unitLiteral) - Block(List(DefDef(sym, rhs)), call) - } - - def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = - ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(ctx.owner, expr)) - - def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef = - ta.assignType(untpd.PackageDef(pid, stats), pid) - - def Annotated(arg: Tree, annot: Tree)(implicit ctx: Context): Annotated = - ta.assignType(untpd.Annotated(arg, annot), arg, annot) - - def Throw(expr: Tree)(implicit ctx: Context): Tree = - ref(defn.throwMethod).appliedTo(expr) - - // ------ Making references ------------------------------------------------------ - - def prefixIsElidable(tp: NamedType)(implicit ctx: Context) = { - val typeIsElidable = tp.prefix match { - case NoPrefix => - true - case pre: ThisType => - pre.cls.isStaticOwner || - tp.symbol.is(ParamOrAccessor) && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls - // was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough - // and was spuriously triggered in case inner class would inherit from outer one - // eg anonymous TypeMap inside TypeMap.andThen - case pre: TermRef => - pre.symbol.is(Module) && pre.symbol.isStatic - case _ => - false - } - typeIsElidable || - tp.symbol.is(JavaStatic) || - tp.symbol.hasAnnotation(defn.ScalaStaticAnnot) - } - - def needsSelect(tp: Type)(implicit ctx: Context) = tp match { - case tp: TermRef => !prefixIsElidable(tp) - case _ => false - } - - /** A tree representing the same reference as the given type */ - def ref(tp: NamedType)(implicit ctx: Context): Tree = - if (tp.isType) TypeTree(tp) - else if (prefixIsElidable(tp)) Ident(tp) - else if (tp.symbol.is(Module) && ctx.owner.isContainedIn(tp.symbol.moduleClass)) - followOuterLinks(This(tp.symbol.moduleClass.asClass)) - else if (tp.symbol hasAnnotation defn.ScalaStaticAnnot) - Ident(tp) - else tp.prefix match { - case pre: SingletonType => followOuterLinks(singleton(pre)).select(tp) - case pre => Select(TypeTree(pre), tp) - } // no checks necessary - - def ref(sym: Symbol)(implicit ctx: Context): Tree = - ref(NamedType(sym.owner.thisType, sym.name, sym.denot)) - - private def followOuterLinks(t: Tree)(implicit ctx: Context) = t match { - case t: This if ctx.erasedTypes && !(t.symbol == ctx.owner.enclosingClass || t.symbol.isStaticOwner) => - // after erasure outer paths should be respected - new ExplicitOuter.OuterOps(ctx).path(t.tpe.widen.classSymbol) - case t => - t - } - - def singleton(tp: Type)(implicit ctx: Context): Tree = tp match { - case tp: TermRef => ref(tp) - case tp: ThisType => This(tp.cls) - case tp: SkolemType => singleton(tp.narrow) - case SuperType(qual, _) => singleton(qual) - case ConstantType(value) => Literal(value) - } - - /** A tree representing a `newXYZArray` operation of the right - * kind for the given element type in `typeArg`. No type arguments or - * `length` arguments are given. - */ - def newArray(elemTpe: Type, returnTpe: Type, pos: Position, dims: JavaSeqLiteral)(implicit ctx: Context): Tree = { - val elemClass = elemTpe.classSymbol - def newArr = - ref(defn.DottyArraysModule).select(defn.newArrayMethod).withPos(pos) - - if (!ctx.erasedTypes) { - assert(!TypeErasure.isUnboundedGeneric(elemTpe)) //needs to be done during typer. See Applications.convertNewGenericArray - newArr.appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) - } else // after erasure - newArr.appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) - } - - // ------ Creating typed equivalents of trees that exist only in untyped form ------- - - /** new C(args), calling the primary constructor of C */ - def New(tp: Type, args: List[Tree])(implicit ctx: Context): Apply = - New(tp, tp.typeSymbol.primaryConstructor.asTerm, args) - - /** new C(args), calling given constructor `constr` of C */ - def New(tp: Type, constr: TermSymbol, args: List[Tree])(implicit ctx: Context): Apply = { - val targs = tp.argTypes - val tycon = tp.withoutArgs(targs) - New(tycon) - .select(TermRef.withSig(tycon, constr)) - .appliedToTypes(targs) - .appliedToArgs(args) - } - - /** An object def - * - * object obs extends parents { decls } - * - * gets expanded to - * - * <module> val obj = new obj$ - * <module> class obj$ extends parents { this: obj.type => decls } - * - * (The following no longer applies: - * What's interesting here is that the block is well typed - * (because class obj$ is hoistable), but the type of the `obj` val is - * not expressible. What needs to happen in general when - * inferring the type of a val from its RHS, is: if the type contains - * a class that has the val itself as owner, then that class - * is remapped to have the val's owner as owner. Remapping could be - * done by cloning the class with the new owner and substituting - * everywhere in the tree. We know that remapping is safe - * because the only way a local class can appear in the RHS of a val is - * by being hoisted outside of a block, and the necessary checks are - * done at this point already. - * - * On the other hand, for method result type inference, if the type of - * the RHS of a method contains a class owned by the method, this would be - * an error.) - */ - def ModuleDef(sym: TermSymbol, body: List[Tree])(implicit ctx: Context): tpd.Thicket = { - val modcls = sym.moduleClass.asClass - val constrSym = modcls.primaryConstructor orElse ctx.newDefaultConstructor(modcls).entered - val constr = DefDef(constrSym.asTerm, EmptyTree) - val clsdef = ClassDef(modcls, constr, body) - val valdef = ValDef(sym, New(modcls.typeRef).select(constrSym).appliedToNone) - Thicket(valdef, clsdef) - } - - /** A `_' with given type */ - def Underscore(tp: Type)(implicit ctx: Context) = untpd.Ident(nme.WILDCARD).withType(tp) - - def defaultValue(tpe: Types.Type)(implicit ctx: Context) = { - val tpw = tpe.widen - - if (tpw isRef defn.IntClass) Literal(Constant(0)) - else if (tpw isRef defn.LongClass) Literal(Constant(0L)) - else if (tpw isRef defn.BooleanClass) Literal(Constant(false)) - else if (tpw isRef defn.CharClass) Literal(Constant('\u0000')) - else if (tpw isRef defn.FloatClass) Literal(Constant(0f)) - else if (tpw isRef defn.DoubleClass) Literal(Constant(0d)) - else if (tpw isRef defn.ByteClass) Literal(Constant(0.toByte)) - else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort)) - else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe) - } - - private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] { - def apply(sym: Symbol, tree: Tree)(implicit ctx: Context) = - if (sym.exists) sym - else if (tree.isDef) { - val owner = tree.symbol.owner - if (owner.isLocalDummy && owner.owner == cls) owner - else if (owner == cls) foldOver(sym, tree) - else sym - } else foldOver(sym, tree) - } - - override val cpy = new TypedTreeCopier - - class TypedTreeCopier extends TreeCopier { - def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] = - copied.withTypeUnchecked(tree.tpe) - def postProcess(tree: Tree, copied: untpd.MemberDef): copied.ThisTree[Type] = - copied.withTypeUnchecked(tree.tpe) - - override def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = { - val tree1 = untpd.cpy.Select(tree)(qualifier, name) - tree match { - case tree: Select if qualifier.tpe eq tree.qualifier.tpe => - tree1.withTypeUnchecked(tree.tpe) - case _ => tree.tpe match { - case tpe: NamedType => tree1.withType(tpe.derivedSelect(qualifier.tpe)) - case _ => tree1.withTypeUnchecked(tree.tpe) - } - } - } - - override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = - ta.assignType(untpd.cpy.Apply(tree)(fun, args), fun, args) - // Note: Reassigning the original type if `fun` and `args` have the same types as before - // does not work here: The computed type depends on the widened function type, not - // the function type itself. A treetransform may keep the function type the - // same but its widened type might change. - - override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = - ta.assignType(untpd.cpy.TypeApply(tree)(fun, args), fun, args) - // Same remark as for Apply - - override def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal = - ta.assignType(untpd.cpy.Literal(tree)(const)) - - override def New(tree: Tree)(tpt: Tree)(implicit ctx: Context): New = - ta.assignType(untpd.cpy.New(tree)(tpt), tpt) - - override def Typed(tree: Tree)(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = - ta.assignType(untpd.cpy.Typed(tree)(expr, tpt), tpt) - - override def NamedArg(tree: Tree)(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = - ta.assignType(untpd.cpy.NamedArg(tree)(name, arg), arg) - - override def Assign(tree: Tree)(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = - ta.assignType(untpd.cpy.Assign(tree)(lhs, rhs)) - - override def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = { - val tree1 = untpd.cpy.Block(tree)(stats, expr) - tree match { - case tree: Block if expr.tpe eq tree.expr.tpe => tree1.withTypeUnchecked(tree.tpe) - case _ => ta.assignType(tree1, stats, expr) - } - } - - override def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = { - val tree1 = untpd.cpy.If(tree)(cond, thenp, elsep) - tree match { - case tree: If if (thenp.tpe eq tree.thenp.tpe) && (elsep.tpe eq tree.elsep.tpe) => tree1.withTypeUnchecked(tree.tpe) - case _ => ta.assignType(tree1, thenp, elsep) - } - } - - override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = - ta.assignType(untpd.cpy.Closure(tree)(env, meth, tpt), meth, tpt) - // Same remark as for Apply - - override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = { - val tree1 = untpd.cpy.Match(tree)(selector, cases) - tree match { - case tree: Match if sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe) - case _ => ta.assignType(tree1, cases) - } - } - - override def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = { - val tree1 = untpd.cpy.CaseDef(tree)(pat, guard, body) - tree match { - case tree: CaseDef if body.tpe eq tree.body.tpe => tree1.withTypeUnchecked(tree.tpe) - case _ => ta.assignType(tree1, body) - } - } - - override def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return = - ta.assignType(untpd.cpy.Return(tree)(expr, from)) - - override def Try(tree: Tree)(expr: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try = { - val tree1 = untpd.cpy.Try(tree)(expr, cases, finalizer) - tree match { - case tree: Try if (expr.tpe eq tree.expr.tpe) && sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe) - case _ => ta.assignType(tree1, expr, cases) - } - } - - override def SeqLiteral(tree: Tree)(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = { - val tree1 = untpd.cpy.SeqLiteral(tree)(elems, elemtpt) - tree match { - case tree: SeqLiteral - if sameTypes(elems, tree.elems) && (elemtpt.tpe eq tree.elemtpt.tpe) => - tree1.withTypeUnchecked(tree.tpe) - case _ => - ta.assignType(tree1, elems, elemtpt) - } - } - - override def Annotated(tree: Tree)(arg: Tree, annot: Tree)(implicit ctx: Context): Annotated = { - val tree1 = untpd.cpy.Annotated(tree)(arg, annot) - tree match { - case tree: Annotated if (arg.tpe eq tree.arg.tpe) && (annot eq tree.annot) => tree1.withTypeUnchecked(tree.tpe) - case _ => ta.assignType(tree1, arg, annot) - } - } - - override def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If = - If(tree: Tree)(cond, thenp, elsep) - override def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure = - Closure(tree: Tree)(env, meth, tpt) - override def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef = - CaseDef(tree: Tree)(pat, guard, body) - override def Try(tree: Try)(expr: Tree = tree.expr, cases: List[CaseDef] = tree.cases, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try = - Try(tree: Tree)(expr, cases, finalizer) - } - - implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal { - - def isValue(implicit ctx: Context): Boolean = - tree.isTerm && tree.tpe.widen.isValueType - - def isValueOrPattern(implicit ctx: Context) = - tree.isValue || tree.isPattern - - def isValueType: Boolean = - tree.isType && tree.tpe.isValueType - - def isInstantiation: Boolean = tree match { - case Apply(Select(New(_), nme.CONSTRUCTOR), _) => true - case _ => false - } - - def shallowFold[T](z: T)(op: (T, tpd.Tree) => T)(implicit ctx: Context) = - new ShallowFolder(op).apply(z, tree) - - def deepFold[T](z: T)(op: (T, tpd.Tree) => T)(implicit ctx: Context) = - new DeepFolder(op).apply(z, tree) - - def find[T](pred: (tpd.Tree) => Boolean)(implicit ctx: Context): Option[tpd.Tree] = - shallowFold[Option[tpd.Tree]](None)((accum, tree) => if (pred(tree)) Some(tree) else accum) - - def subst(from: List[Symbol], to: List[Symbol])(implicit ctx: Context): ThisTree = - new TreeTypeMap(substFrom = from, substTo = to).apply(tree) - - /** Change owner from `from` to `to`. If `from` is a weak owner, also change its - * owner to `to`, and continue until a non-weak owner is reached. - */ - def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = { - def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = { - if (from.isWeakOwner && !from.owner.isClass) - loop(from.owner, from :: froms, to :: tos) - else { - //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") - new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree) - } - } - loop(from, Nil, to :: Nil) - } - - /** After phase `trans`, set the owner of every definition in this tree that was formerly - * owner by `from` to `to`. - */ - def changeOwnerAfter(from: Symbol, to: Symbol, trans: DenotTransformer)(implicit ctx: Context): ThisTree = { - assert(ctx.phase == trans.next) - val traverser = new TreeTraverser { - def traverse(tree: Tree)(implicit ctx: Context) = tree match { - case tree: DefTree => - val sym = tree.symbol - val prevDenot = sym.denot(ctx.withPhase(trans)) - if (prevDenot.owner == from) { - val d = sym.copySymDenotation(owner = to) - d.installAfter(trans) - d.transformAfter(trans, d => if (d.owner eq from) d.copySymDenotation(owner = to) else d) - } - if (sym.isWeakOwner) traverseChildren(tree) - case _ => - traverseChildren(tree) - } - } - traverser.traverse(tree) - tree - } - - /** A select node with the given selector name and a computed type */ - def select(name: Name)(implicit ctx: Context): Select = - Select(tree, name) - - /** A select node with the given type */ - def select(tp: NamedType)(implicit ctx: Context): Select = - untpd.Select(tree, tp.name).withType(tp) - - /** A select node that selects the given symbol. Note: Need to make sure this - * is in fact the symbol you would get when you select with the symbol's name, - * otherwise a data race may occur which would be flagged by -Yno-double-bindings. - */ - def select(sym: Symbol)(implicit ctx: Context): Select = { - val tp = - if (sym.isType) - TypeRef(tree.tpe, sym.name.asTypeName) - else - TermRef.withSigAndDenot(tree.tpe, sym.name.asTermName, - sym.signature, sym.denot.asSeenFrom(tree.tpe)) - untpd.Select(tree, sym.name) - .withType(tp) - } - - /** A select node with the given selector name and signature and a computed type */ - def selectWithSig(name: Name, sig: Signature)(implicit ctx: Context): Tree = - untpd.SelectWithSig(tree, name, sig) - .withType(TermRef.withSig(tree.tpe, name.asTermName, sig)) - - /** A select node with selector name and signature taken from `sym`. - * Note: Use this method instead of select(sym) if the referenced symbol - * might be overridden in the type of the qualifier prefix. See note - * on select(sym: Symbol). - */ - def selectWithSig(sym: Symbol)(implicit ctx: Context): Tree = - selectWithSig(sym.name, sym.signature) - - /** A unary apply node with given argument: `tree(arg)` */ - def appliedTo(arg: Tree)(implicit ctx: Context): Tree = - appliedToArgs(arg :: Nil) - - /** An apply node with given arguments: `tree(arg, args0, ..., argsN)` */ - def appliedTo(arg: Tree, args: Tree*)(implicit ctx: Context): Tree = - appliedToArgs(arg :: args.toList) - - /** An apply node with given argument list `tree(args(0), ..., args(args.length - 1))` */ - def appliedToArgs(args: List[Tree])(implicit ctx: Context): Apply = - Apply(tree, args) - - /** The current tree applied to given argument lists: - * `tree (argss(0)) ... (argss(argss.length -1))` - */ - def appliedToArgss(argss: List[List[Tree]])(implicit ctx: Context): Tree = - ((tree: Tree) /: argss)(Apply(_, _)) - - /** The current tree applied to (): `tree()` */ - def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil) - - /** The current tree applied to given type argument: `tree[targ]` */ - def appliedToType(targ: Type)(implicit ctx: Context): Tree = - appliedToTypes(targ :: Nil) - - /** The current tree applied to given type arguments: `tree[targ0, ..., targN]` */ - def appliedToTypes(targs: List[Type])(implicit ctx: Context): Tree = - appliedToTypeTrees(targs map (TypeTree(_))) - - /** The current tree applied to given type argument list: `tree[targs(0), ..., targs(targs.length - 1)]` */ - def appliedToTypeTrees(targs: List[Tree])(implicit ctx: Context): Tree = - if (targs.isEmpty) tree else TypeApply(tree, targs) - - /** Apply to `()` unless tree's widened type is parameterless */ - def ensureApplied(implicit ctx: Context): Tree = - if (tree.tpe.widen.isParameterless) tree else tree.appliedToNone - - /** `tree.isInstanceOf[tp]` */ - def isInstance(tp: Type)(implicit ctx: Context): Tree = - tree.select(defn.Any_isInstanceOf).appliedToType(tp) - - /** tree.asInstanceOf[`tp`] */ - def asInstance(tp: Type)(implicit ctx: Context): Tree = { - assert(tp.isValueType, i"bad cast: $tree.asInstanceOf[$tp]") - tree.select(defn.Any_asInstanceOf).appliedToType(tp) - } - - /** `tree.asInstanceOf[tp]` (or its box/unbox/cast equivalent when after - * erasure and value and non-value types are mixed), - * unless tree's type already conforms to `tp`. - */ - def ensureConforms(tp: Type)(implicit ctx: Context): Tree = - if (tree.tpe <:< tp) tree - else if (!ctx.erasedTypes) asInstance(tp) - else Erasure.Boxing.adaptToType(tree, tp) - - /** If inititializer tree is `_', the default value of its type, - * otherwise the tree itself. - */ - def wildcardToDefault(implicit ctx: Context) = - if (isWildcardArg(tree)) defaultValue(tree.tpe) else tree - - /** `this && that`, for boolean trees `this`, `that` */ - def and(that: Tree)(implicit ctx: Context): Tree = - tree.select(defn.Boolean_&&).appliedTo(that) - - /** `this || that`, for boolean trees `this`, `that` */ - def or(that: Tree)(implicit ctx: Context): Tree = - tree.select(defn.Boolean_||).appliedTo(that) - - /** The translation of `tree = rhs`. - * This is either the tree as an assignment, to a setter call. - */ - def becomes(rhs: Tree)(implicit ctx: Context): Tree = - if (tree.symbol is Method) { - val setr = tree match { - case Ident(_) => - val setter = tree.symbol.setter - assert(setter.exists, tree.symbol.showLocated) - ref(tree.symbol.setter) - case Select(qual, _) => qual.select(tree.symbol.setter) - } - setr.appliedTo(rhs) - } - else Assign(tree, rhs) - - // --- Higher order traversal methods ------------------------------- - - /** Apply `f` to each subtree of this tree */ - def foreachSubTree(f: Tree => Unit)(implicit ctx: Context): Unit = { - val traverser = new TreeTraverser { - def traverse(tree: Tree)(implicit ctx: Context) = foldOver(f(tree), tree) - } - traverser.traverse(tree) - } - - /** Is there a subtree of this tree that satisfies predicate `p`? */ - def existsSubTree(p: Tree => Boolean)(implicit ctx: Context): Boolean = { - val acc = new TreeAccumulator[Boolean] { - def apply(x: Boolean, t: Tree)(implicit ctx: Context) = x || p(t) || foldOver(x, t) - } - acc(false, tree) - } - - /** All subtrees of this tree that satisfy predicate `p`. */ - def filterSubTrees(f: Tree => Boolean)(implicit ctx: Context): List[Tree] = { - val buf = new mutable.ListBuffer[Tree] - foreachSubTree { tree => if (f(tree)) buf += tree } - buf.toList - } - } - - implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal { - def tpes: List[Type] = xs map (_.tpe) - } - - // convert a numeric with a toXXX method - def primitiveConversion(tree: Tree, numericCls: Symbol)(implicit ctx: Context): Tree = { - val mname = ("to" + numericCls.name).toTermName - val conversion = tree.tpe member mname - if (conversion.symbol.exists) - tree.select(conversion.symbol.termRef).ensureApplied - else if (tree.tpe.widen isRef numericCls) - tree - else { - ctx.warning(i"conversion from ${tree.tpe.widen} to ${numericCls.typeRef} will always fail at runtime.") - Throw(New(defn.ClassCastExceptionClass.typeRef, Nil)) withPos tree.pos - } - } - - /** A tree that represents the class of the erasure of type `tp`. */ - def clsOf(tp: Type)(implicit ctx: Context): Tree = { - def TYPE(module: TermSymbol) = ref(module).select(nme.TYPE_) - defn.scalaClassName(tp) match { - case tpnme.Boolean => TYPE(defn.BoxedBooleanModule) - case tpnme.Byte => TYPE(defn.BoxedByteModule) - case tpnme.Short => TYPE(defn.BoxedShortModule) - case tpnme.Char => TYPE(defn.BoxedCharModule) - case tpnme.Int => TYPE(defn.BoxedIntModule) - case tpnme.Long => TYPE(defn.BoxedLongModule) - case tpnme.Float => TYPE(defn.BoxedFloatModule) - case tpnme.Double => TYPE(defn.BoxedDoubleModule) - case tpnme.Unit => TYPE(defn.BoxedUnitModule) - case _ => - if(ctx.erasedTypes || !tp.derivesFrom(defn.ArrayClass)) - Literal(Constant(TypeErasure.erasure(tp))) - else Literal(Constant(tp)) - } - } - - def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = { - val typer = ctx.typer - val proto = new FunProtoTyped(args, expectedType, typer) - val denot = receiver.tpe.member(method) - assert(denot.exists, i"no member $receiver . $method, members = ${receiver.tpe.decls}") - val selected = - if (denot.isOverloaded) { - def typeParamCount(tp: Type) = tp.widen match { - case tp: PolyType => tp.paramBounds.length - case _ => 0 - } - var allAlts = denot.alternatives - .map(_.termRef).filter(tr => typeParamCount(tr) == targs.length) - if (targs.isEmpty) allAlts = allAlts.filterNot(_.widen.isInstanceOf[PolyType]) - val alternatives = ctx.typer.resolveOverloaded(allAlts, proto) - assert(alternatives.size == 1, - i"${if (alternatives.isEmpty) "no" else "multiple"} overloads available for " + - i"$method on ${receiver.tpe.widenDealias} with targs: $targs%, %; args: $args%, % of types ${args.tpes}%, %; expectedType: $expectedType." + - i" isAnnotConstructor = $isAnnotConstructor.\n" + - i"all alternatives: ${allAlts.map(_.symbol.showDcl).mkString(", ")}\n" + - i"matching alternatives: ${alternatives.map(_.symbol.showDcl).mkString(", ")}.") // this is parsed from bytecode tree. there's nothing user can do about it - alternatives.head - } - else denot.asSingleDenotation.termRef - val fun = receiver - .select(TermRef.withSig(receiver.tpe, selected.termSymbol.asTerm)) - .appliedToTypes(targs) - - def adaptLastArg(lastParam: Tree, expectedType: Type) = { - if (isAnnotConstructor && !(lastParam.tpe <:< expectedType)) { - val defn = ctx.definitions - val prefix = args.take(selected.widen.paramTypess.head.size - 1) - expectedType match { - case defn.ArrayOf(el) => - lastParam.tpe match { - case defn.ArrayOf(el2) if el2 <:< el => - // we have a JavaSeqLiteral with a more precise type - // we cannot construct a tree as JavaSeqLiteral infered to precise type - // if we add typed than it would be both type-correct and - // will pass Ycheck - prefix ::: List(tpd.Typed(lastParam, TypeTree(defn.ArrayOf(el)))) - case _ => - ??? - } - case _ => args - } - } else args - } - - val callArgs: List[Tree] = if (args.isEmpty) Nil else { - val expectedType = selected.widen.paramTypess.head.last - val lastParam = args.last - adaptLastArg(lastParam, expectedType) - } - - val apply = untpd.Apply(fun, callArgs) - new typer.ApplyToTyped(apply, fun, selected, callArgs, expectedType).result.asInstanceOf[Tree] // needed to handle varargs - } - - @tailrec - def sameTypes(trees: List[tpd.Tree], trees1: List[tpd.Tree]): Boolean = { - if (trees.isEmpty) trees.isEmpty - else if (trees1.isEmpty) trees.isEmpty - else (trees.head.tpe eq trees1.head.tpe) && sameTypes(trees.tail, trees1.tail) - } - - def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = { - if (isIdempotentExpr(tree)) within(tree) - else { - val vdef = SyntheticValDef(ctx.freshName("ev$").toTermName, tree) - Block(vdef :: Nil, within(Ident(vdef.namedType))) - } - } - - def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = { - Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args) - } - - /** An extractor that pulls out type arguments */ - object MaybePoly { - def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match { - case TypeApply(tree, targs) => Some(tree, targs) - case _ => Some(tree, Nil) - } - } - - /** A traverser that passes the enclosing class or method as an argument - * to the traverse method. - */ - abstract class EnclosingMethodTraverser extends TreeAccumulator[Symbol] { - def traverse(enclMeth: Symbol, tree: Tree)(implicit ctx: Context): Unit - def apply(enclMeth: Symbol, tree: Tree)(implicit ctx: Context) = { - tree match { - case _: DefTree if tree.symbol.exists => - traverse(tree.symbol.enclosingMethod, tree) - case _ => - traverse(enclMeth, tree) - } - enclMeth - } - } - - /** A key to be used in a context property that tracks enclosing inlined calls */ - private val InlinedCalls = new Property.Key[List[Tree]] - - /** A context derived form `ctx` that records `call` as innermost enclosing - * call for which the inlined version is currently processed. - */ - def inlineContext(call: Tree)(implicit ctx: Context): Context = - ctx.fresh.setProperty(InlinedCalls, call :: enclosingInlineds) - - /** All enclosing calls that are currently inlined, from innermost to outermost */ - def enclosingInlineds(implicit ctx: Context): List[Tree] = - ctx.property(InlinedCalls).getOrElse(Nil) - - /** The source file where the symbol of the `@inline` method referred to by `call` - * is defined - */ - def sourceFile(call: Tree)(implicit ctx: Context) = { - val file = call.symbol.sourceFile - val encoding = ctx.settings.encoding.value - if (file != null && file.exists) new SourceFile(file, Codec(encoding)) else NoSource - } -} - diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala deleted file mode 100644 index 6c5210287..000000000 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ /dev/null @@ -1,562 +0,0 @@ -package dotty.tools -package dotc -package ast - -import core._ -import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ -import Denotations._, SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ -import Decorators._ -import util.Property -import language.higherKinds -import collection.mutable.ListBuffer - -object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { - - // ----- Tree cases that exist in untyped form only ------------------ - - trait OpTree extends Tree { - def op: Name - override def isTerm = op.isTermName - override def isType = op.isTypeName - } - - /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice - * @param owner The current owner at the time the tree was defined - */ - abstract case class TypedSplice(tree: tpd.Tree)(val owner: Symbol) extends ProxyTree { - def forwardTo = tree - } - - object TypedSplice { - def apply(tree: tpd.Tree)(implicit ctx: Context): TypedSplice = - new TypedSplice(tree)(ctx.owner) {} - } - - /** mods object name impl */ - case class ModuleDef(name: TermName, impl: Template) - extends MemberDef { - type ThisTree[-T >: Untyped] <: Trees.NameTree[T] with Trees.MemberDef[T] with ModuleDef - def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(name.toTermName, impl) - } - - case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree) extends TermTree - - case class SymbolLit(str: String) extends TermTree - - /** An interpolated string - * @param segments a list of two element tickets consisting of string literal and argument tree, - * possibly with a simple string literal as last element of the list - */ - case class InterpolatedString(id: TermName, segments: List[Tree]) extends TermTree - - case class Function(args: List[Tree], body: Tree) extends Tree { - override def isTerm = body.isTerm - override def isType = body.isType - } - /** A function created from a wildcard expression - * @param placeHolderParams a list of definitions of synthetic parameters - * @param body the function body where wildcards are replaced by - * references to synthetic parameters. - */ - class WildcardFunction(placeholderParams: List[ValDef], body: Tree) extends Function(placeholderParams, body) - - case class InfixOp(left: Tree, op: Name, right: Tree) extends OpTree - case class PostfixOp(od: Tree, op: Name) extends OpTree - case class PrefixOp(op: Name, od: Tree) extends OpTree - case class Parens(t: Tree) extends ProxyTree { - def forwardTo = t - } - case class Tuple(trees: List[Tree]) extends Tree { - override def isTerm = trees.isEmpty || trees.head.isTerm - override def isType = !isTerm - } - case class Throw(expr: Tree) extends TermTree - case class WhileDo(cond: Tree, body: Tree) extends TermTree - case class DoWhile(body: Tree, cond: Tree) extends TermTree - case class ForYield(enums: List[Tree], expr: Tree) extends TermTree - case class ForDo(enums: List[Tree], body: Tree) extends TermTree - case class GenFrom(pat: Tree, expr: Tree) extends Tree - case class GenAlias(pat: Tree, expr: Tree) extends Tree - case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree]) extends TypTree - case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) extends DefTree - - @sharable object EmptyTypeIdent extends Ident(tpnme.EMPTY) with WithoutTypeOrPos[Untyped] { - override def isEmpty = true - } - - /** A block arising from a right-associative infix operation, where, e.g. - * - * a +: b - * - * is expanded to - * - * { val x = a; b.+:(x) } - */ - class InfixOpBlock(leftOperand: Tree, rightOp: Tree) extends Block(leftOperand :: Nil, rightOp) - - // ----- Modifiers ----------------------------------------------------- - /** Mod is intended to record syntactic information about modifiers, it's - * NOT a replacement of FlagSet. - * - * For any query about semantic information, check `flags` instead. - */ - sealed abstract class Mod(val flags: FlagSet) extends Positioned - - object Mod { - case class Private() extends Mod(Flags.Private) - - case class Protected() extends Mod(Flags.Protected) - - case class Val() extends Mod(Flags.EmptyFlags) - - case class Var() extends Mod(Flags.Mutable) - - case class Implicit(flag: FlagSet = Flags.ImplicitCommon) extends Mod(flag) - - case class Final() extends Mod(Flags.Final) - - case class Sealed() extends Mod(Flags.Sealed) - - case class Override() extends Mod(Flags.Override) - - case class Abstract() extends Mod(Flags.Abstract) - - case class Lazy() extends Mod(Flags.Lazy) - - case class Inline() extends Mod(Flags.Inline) - - case class Type() extends Mod(Flags.EmptyFlags) - } - - /** Modifiers and annotations for definitions - * - * @param flags The set flags - * @param privateWithin If a private or protected has is followed by a - * qualifier [q], the name q, "" as a typename otherwise. - * @param annotations The annotations preceding the modifiers - */ - case class Modifiers ( - flags: FlagSet = EmptyFlags, - privateWithin: TypeName = tpnme.EMPTY, - annotations: List[Tree] = Nil, - mods: List[Mod] = Nil) extends Positioned with Cloneable { - - def is(fs: FlagSet): Boolean = flags is fs - def is(fc: FlagConjunction): Boolean = flags is fc - - def | (fs: FlagSet): Modifiers = withFlags(flags | fs) - def & (fs: FlagSet): Modifiers = withFlags(flags & fs) - def &~(fs: FlagSet): Modifiers = withFlags(flags &~ fs) - - def toTypeFlags: Modifiers = withFlags(flags.toTypeFlags) - def toTermFlags: Modifiers = withFlags(flags.toTermFlags) - - def withFlags(flags: FlagSet) = - if (this.flags == flags) this - else copy(flags = flags) - - def withAddedMod(mod: Mod): Modifiers = - if (mods.exists(_ eq mod)) this - else withMods(mods :+ mod) - - def withMods(ms: List[Mod]): Modifiers = - if (mods eq ms) this - else copy(mods = ms) - - def withAddedAnnotation(annot: Tree): Modifiers = - if (annotations.exists(_ eq annot)) this - else withAnnotations(annotations :+ annot) - - def withAnnotations(annots: List[Tree]): Modifiers = - if (annots eq annotations) this - else copy(annotations = annots) - - def withPrivateWithin(pw: TypeName) = - if (pw.isEmpty) this - else copy(privateWithin = pw) - - def hasFlags = flags != EmptyFlags - def hasAnnotations = annotations.nonEmpty - def hasPrivateWithin = privateWithin != tpnme.EMPTY - } - - @sharable val EmptyModifiers: Modifiers = new Modifiers() - - // ----- TypeTrees that refer to other tree's symbols ------------------- - - /** A type tree that gets its type from some other tree's symbol. Enters the - * type tree in the References attachment of the `from` tree as a side effect. - */ - abstract class DerivedTypeTree extends TypeTree { - - private var myWatched: Tree = EmptyTree - - /** The watched tree; used only for printing */ - def watched: Tree = myWatched - - /** Install the derived type tree as a dependency on `original` */ - def watching(original: DefTree): this.type = { - myWatched = original - val existing = original.attachmentOrElse(References, Nil) - original.putAttachment(References, this :: existing) - this - } - - /** A hook to ensure that all necessary symbols are completed so that - * OriginalSymbol attachments are propagated to this tree - */ - def ensureCompletions(implicit ctx: Context): Unit = () - - /** The method that computes the type of this tree */ - def derivedType(originalSym: Symbol)(implicit ctx: Context): Type - } - - /** Property key containing TypeTrees whose type is computed - * from the symbol in this type. These type trees have marker trees - * TypeRefOfSym or InfoOfSym as their originals. - */ - val References = new Property.Key[List[Tree]] - - /** Property key for TypeTrees marked with TypeRefOfSym or InfoOfSym - * which contains the symbol of the original tree from which this - * TypeTree is derived. - */ - val OriginalSymbol = new Property.Key[Symbol] - - // ------ Creation methods for untyped only ----------------- - - def Ident(name: Name): Ident = new Ident(name) - def BackquotedIdent(name: Name): BackquotedIdent = new BackquotedIdent(name) - def Select(qualifier: Tree, name: Name): Select = new Select(qualifier, name) - def SelectWithSig(qualifier: Tree, name: Name, sig: Signature): Select = new SelectWithSig(qualifier, name, sig) - def This(qual: Ident): This = new This(qual) - def Super(qual: Tree, mix: Ident): Super = new Super(qual, mix) - def Apply(fun: Tree, args: List[Tree]): Apply = new Apply(fun, args) - def TypeApply(fun: Tree, args: List[Tree]): TypeApply = new TypeApply(fun, args) - def Literal(const: Constant): Literal = new Literal(const) - def New(tpt: Tree): New = new New(tpt) - def Typed(expr: Tree, tpt: Tree): Typed = new Typed(expr, tpt) - def NamedArg(name: Name, arg: Tree): NamedArg = new NamedArg(name, arg) - def Assign(lhs: Tree, rhs: Tree): Assign = new Assign(lhs, rhs) - def Block(stats: List[Tree], expr: Tree): Block = new Block(stats, expr) - def If(cond: Tree, thenp: Tree, elsep: Tree): If = new If(cond, thenp, elsep) - def Closure(env: List[Tree], meth: Tree, tpt: Tree): Closure = new Closure(env, meth, tpt) - def Match(selector: Tree, cases: List[CaseDef]): Match = new Match(selector, cases) - def CaseDef(pat: Tree, guard: Tree, body: Tree): CaseDef = new CaseDef(pat, guard, body) - def Return(expr: Tree, from: Tree): Return = new Return(expr, from) - def Try(expr: Tree, cases: List[CaseDef], finalizer: Tree): Try = new Try(expr, cases, finalizer) - def SeqLiteral(elems: List[Tree], elemtpt: Tree): SeqLiteral = new SeqLiteral(elems, elemtpt) - def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) - def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree): Inlined = new Inlined(call, bindings, expansion) - def TypeTree() = new TypeTree() - def SingletonTypeTree(ref: Tree): SingletonTypeTree = new SingletonTypeTree(ref) - def AndTypeTree(left: Tree, right: Tree): AndTypeTree = new AndTypeTree(left, right) - def OrTypeTree(left: Tree, right: Tree): OrTypeTree = new OrTypeTree(left, right) - def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements) - def AppliedTypeTree(tpt: Tree, args: List[Tree]): AppliedTypeTree = new AppliedTypeTree(tpt, args) - def PolyTypeTree(tparams: List[TypeDef], body: Tree): PolyTypeTree = new PolyTypeTree(tparams, body) - def ByNameTypeTree(result: Tree): ByNameTypeTree = new ByNameTypeTree(result) - def TypeBoundsTree(lo: Tree, hi: Tree): TypeBoundsTree = new TypeBoundsTree(lo, hi) - def Bind(name: Name, body: Tree): Bind = new Bind(name, body) - def Alternative(trees: List[Tree]): Alternative = new Alternative(trees) - def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = new UnApply(fun, implicits, patterns) - def ValDef(name: TermName, tpt: Tree, rhs: LazyTree): ValDef = new ValDef(name, tpt, rhs) - def DefDef(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree): DefDef = new DefDef(name, tparams, vparamss, tpt, rhs) - def TypeDef(name: TypeName, rhs: Tree): TypeDef = new TypeDef(name, rhs) - def Template(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList): Template = new Template(constr, parents, self, body) - def Import(expr: Tree, selectors: List[untpd.Tree]): Import = new Import(expr, selectors) - def PackageDef(pid: RefTree, stats: List[Tree]): PackageDef = new PackageDef(pid, stats) - def Annotated(arg: Tree, annot: Tree): Annotated = new Annotated(arg, annot) - - // ------ Additional creation methods for untyped only ----------------- - - // def TypeTree(tpe: Type): TypeTree = TypeTree().withType(tpe) todo: move to untpd/tpd - - /** new pre.C[Ts](args1)...(args_n) - * ==> - * (new pre.C).<init>[Ts](args1)...(args_n) - */ - def New(tpt: Tree, argss: List[List[Tree]])(implicit ctx: Context): Tree = { - val (tycon, targs) = tpt match { - case AppliedTypeTree(tycon, targs) => - (tycon, targs) - case TypedSplice(AppliedTypeTree(tycon, targs)) => - (TypedSplice(tycon), targs map (TypedSplice(_))) - case TypedSplice(tpt1: Tree) => - val argTypes = tpt1.tpe.argTypesLo - val tycon = tpt1.tpe.withoutArgs(argTypes) - def wrap(tpe: Type) = TypeTree(tpe) withPos tpt.pos - (wrap(tycon), argTypes map wrap) - case _ => - (tpt, Nil) - } - var prefix: Tree = Select(New(tycon), nme.CONSTRUCTOR) - if (targs.nonEmpty) prefix = TypeApply(prefix, targs) - ensureApplied((prefix /: argss)(Apply(_, _))) - } - - def Block(stat: Tree, expr: Tree): Block = - Block(stat :: Nil, expr) - - def Apply(fn: Tree, arg: Tree): Apply = - Apply(fn, arg :: Nil) - - def ensureApplied(tpt: Tree) = tpt match { - case _: Apply => tpt - case _ => Apply(tpt, Nil) - } - - def AppliedTypeTree(tpt: Tree, arg: Tree): AppliedTypeTree = - AppliedTypeTree(tpt, arg :: Nil) - - def TypeTree(tpe: Type)(implicit ctx: Context): TypedSplice = TypedSplice(TypeTree().withTypeUnchecked(tpe)) - - def unitLiteral = Literal(Constant(())) - - def ref(tp: NamedType)(implicit ctx: Context): Tree = - TypedSplice(tpd.ref(tp)) - - def rootDot(name: Name) = Select(Ident(nme.ROOTPKG), name) - def scalaDot(name: Name) = Select(rootDot(nme.scala_), name) - def scalaUnit = scalaDot(tpnme.Unit) - def scalaAny = scalaDot(tpnme.Any) - - def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = - DefDef(nme.CONSTRUCTOR, tparams, vparamss, TypeTree(), rhs) - - def emptyConstructor(implicit ctx: Context): DefDef = - makeConstructor(Nil, Nil) - - def makeSelfDef(name: TermName, tpt: Tree)(implicit ctx: Context) = - ValDef(name, tpt, EmptyTree).withFlags(PrivateLocal) - - def makeTupleOrParens(ts: List[Tree])(implicit ctx: Context) = ts match { - case t :: Nil => Parens(t) - case _ => Tuple(ts) - } - - def makeTuple(ts: List[Tree])(implicit ctx: Context) = ts match { - case t :: Nil => t - case _ => Tuple(ts) - } - - def makeParameter(pname: TermName, tpe: Tree, mods: Modifiers = EmptyModifiers)(implicit ctx: Context): ValDef = - ValDef(pname, tpe, EmptyTree).withMods(mods | Param) - - def makeSyntheticParameter(n: Int = 1, tpt: Tree = TypeTree())(implicit ctx: Context): ValDef = - ValDef(nme.syntheticParamName(n), tpt, EmptyTree).withFlags(SyntheticTermParam) - - def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context) = - if (tparams.isEmpty) tpt else PolyTypeTree(tparams, tpt) - - /** A reference to given definition. If definition is a repeated - * parameter, the reference will be a repeated argument. - */ - def refOfDef(tree: MemberDef)(implicit ctx: Context) = tree match { - case ValDef(_, PostfixOp(_, nme.raw.STAR), _) => repeated(Ident(tree.name)) - case _ => Ident(tree.name) - } - - /** A repeated argument such as `arg: _*` */ - def repeated(arg: Tree)(implicit ctx: Context) = Typed(arg, Ident(tpnme.WILDCARD_STAR)) - -// ----- Accessing modifiers ---------------------------------------------------- - - abstract class ModsDecorator { def mods: Modifiers } - - implicit class modsDeco(val mdef: MemberDef)(implicit ctx: Context) { - def mods = mdef.rawMods - } - -// --------- Copier/Transformer/Accumulator classes for untyped trees ----- - - override val cpy: UntypedTreeCopier = new UntypedTreeCopier - - class UntypedTreeCopier extends TreeCopier { - - def postProcess(tree: Tree, copied: Tree): copied.ThisTree[Untyped] = - copied.asInstanceOf[copied.ThisTree[Untyped]] - - def postProcess(tree: Tree, copied: MemberDef): copied.ThisTree[Untyped] = { - tree match { - case tree: MemberDef => copied.withMods(tree.rawMods) - case _ => copied - } - }.asInstanceOf[copied.ThisTree[Untyped]] - - def ModuleDef(tree: Tree)(name: TermName, impl: Template) = tree match { - case tree: ModuleDef if (name eq tree.name) && (impl eq tree.impl) => tree - case _ => untpd.ModuleDef(name, impl).withPos(tree.pos) - } - def ParsedTry(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree) = tree match { - case tree: ParsedTry - if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree - case _ => untpd.ParsedTry(expr, handler, finalizer).withPos(tree.pos) - } - def SymbolLit(tree: Tree)(str: String) = tree match { - case tree: SymbolLit if str == tree.str => tree - case _ => untpd.SymbolLit(str).withPos(tree.pos) - } - def InterpolatedString(tree: Tree)(id: TermName, segments: List[Tree]) = tree match { - case tree: InterpolatedString if (id eq tree.id) && (segments eq tree.segments) => tree - case _ => untpd.InterpolatedString(id, segments).withPos(tree.pos) - } - def Function(tree: Tree)(args: List[Tree], body: Tree) = tree match { - case tree: Function if (args eq tree.args) && (body eq tree.body) => tree - case _ => untpd.Function(args, body).withPos(tree.pos) - } - def InfixOp(tree: Tree)(left: Tree, op: Name, right: Tree) = tree match { - case tree: InfixOp if (left eq tree.left) && (op eq tree.op) && (right eq tree.right) => tree - case _ => untpd.InfixOp(left, op, right).withPos(tree.pos) - } - def PostfixOp(tree: Tree)(od: Tree, op: Name) = tree match { - case tree: PostfixOp if (od eq tree.od) && (op eq tree.op) => tree - case _ => untpd.PostfixOp(od, op).withPos(tree.pos) - } - def PrefixOp(tree: Tree)(op: Name, od: Tree) = tree match { - case tree: PrefixOp if (op eq tree.op) && (od eq tree.od) => tree - case _ => untpd.PrefixOp(op, od).withPos(tree.pos) - } - def Parens(tree: Tree)(t: Tree) = tree match { - case tree: Parens if t eq tree.t => tree - case _ => untpd.Parens(t).withPos(tree.pos) - } - def Tuple(tree: Tree)(trees: List[Tree]) = tree match { - case tree: Tuple if trees eq tree.trees => tree - case _ => untpd.Tuple(trees).withPos(tree.pos) - } - def Throw(tree: Tree)(expr: Tree) = tree match { - case tree: Throw if expr eq tree.expr => tree - case _ => untpd.Throw(expr).withPos(tree.pos) - } - def WhileDo(tree: Tree)(cond: Tree, body: Tree) = tree match { - case tree: WhileDo if (cond eq tree.cond) && (body eq tree.body) => tree - case _ => untpd.WhileDo(cond, body).withPos(tree.pos) - } - def DoWhile(tree: Tree)(body: Tree, cond: Tree) = tree match { - case tree: DoWhile if (body eq tree.body) && (cond eq tree.cond) => tree - case _ => untpd.DoWhile(body, cond).withPos(tree.pos) - } - def ForYield(tree: Tree)(enums: List[Tree], expr: Tree) = tree match { - case tree: ForYield if (enums eq tree.enums) && (expr eq tree.expr) => tree - case _ => untpd.ForYield(enums, expr).withPos(tree.pos) - } - def ForDo(tree: Tree)(enums: List[Tree], body: Tree) = tree match { - case tree: ForDo if (enums eq tree.enums) && (body eq tree.body) => tree - case _ => untpd.ForDo(enums, body).withPos(tree.pos) - } - def GenFrom(tree: Tree)(pat: Tree, expr: Tree) = tree match { - case tree: GenFrom if (pat eq tree.pat) && (expr eq tree.expr) => tree - case _ => untpd.GenFrom(pat, expr).withPos(tree.pos) - } - def GenAlias(tree: Tree)(pat: Tree, expr: Tree) = tree match { - case tree: GenAlias if (pat eq tree.pat) && (expr eq tree.expr) => tree - case _ => untpd.GenAlias(pat, expr).withPos(tree.pos) - } - def ContextBounds(tree: Tree)(bounds: TypeBoundsTree, cxBounds: List[Tree]) = tree match { - case tree: ContextBounds if (bounds eq tree.bounds) && (cxBounds eq tree.cxBounds) => tree - case _ => untpd.ContextBounds(bounds, cxBounds).withPos(tree.pos) - } - def PatDef(tree: Tree)(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) = tree match { - case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree - case _ => untpd.PatDef(mods, pats, tpt, rhs).withPos(tree.pos) - } - } - - abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) { - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case ModuleDef(name, impl) => - cpy.ModuleDef(tree)(name, transformSub(impl)) - case ParsedTry(expr, handler, finalizer) => - cpy.ParsedTry(tree)(transform(expr), transform(handler), transform(finalizer)) - case SymbolLit(str) => - cpy.SymbolLit(tree)(str) - case InterpolatedString(id, segments) => - cpy.InterpolatedString(tree)(id, transform(segments)) - case Function(args, body) => - cpy.Function(tree)(transform(args), transform(body)) - case InfixOp(left, op, right) => - cpy.InfixOp(tree)(transform(left), op, transform(right)) - case PostfixOp(od, op) => - cpy.PostfixOp(tree)(transform(od), op) - case PrefixOp(op, od) => - cpy.PrefixOp(tree)(op, transform(od)) - case Parens(t) => - cpy.Parens(tree)(transform(t)) - case Tuple(trees) => - cpy.Tuple(tree)(transform(trees)) - case Throw(expr) => - cpy.Throw(tree)(transform(expr)) - case WhileDo(cond, body) => - cpy.WhileDo(tree)(transform(cond), transform(body)) - case DoWhile(body, cond) => - cpy.DoWhile(tree)(transform(body), transform(cond)) - case ForYield(enums, expr) => - cpy.ForYield(tree)(transform(enums), transform(expr)) - case ForDo(enums, body) => - cpy.ForDo(tree)(transform(enums), transform(body)) - case GenFrom(pat, expr) => - cpy.GenFrom(tree)(transform(pat), transform(expr)) - case GenAlias(pat, expr) => - cpy.GenAlias(tree)(transform(pat), transform(expr)) - case ContextBounds(bounds, cxBounds) => - cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds)) - case PatDef(mods, pats, tpt, rhs) => - cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) - case _ => - super.transform(tree) - } - } - - abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] { - override def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = tree match { - case ModuleDef(name, impl) => - this(x, impl) - case ParsedTry(expr, handler, finalizer) => - this(this(this(x, expr), handler), finalizer) - case SymbolLit(str) => - x - case InterpolatedString(id, segments) => - this(x, segments) - case Function(args, body) => - this(this(x, args), body) - case InfixOp(left, op, right) => - this(this(x, left), right) - case PostfixOp(od, op) => - this(x, od) - case PrefixOp(op, od) => - this(x, od) - case Parens(t) => - this(x, t) - case Tuple(trees) => - this(x, trees) - case Throw(expr) => - this(x, expr) - case WhileDo(cond, body) => - this(this(x, cond), body) - case DoWhile(body, cond) => - this(this(x, body), cond) - case ForYield(enums, expr) => - this(this(x, enums), expr) - case ForDo(enums, body) => - this(this(x, enums), body) - case GenFrom(pat, expr) => - this(this(x, pat), expr) - case GenAlias(pat, expr) => - this(this(x, pat), expr) - case ContextBounds(bounds, cxBounds) => - this(this(x, bounds), cxBounds) - case PatDef(mods, pats, tpt, rhs) => - this(this(this(x, pats), tpt), rhs) - case TypedSplice(tree) => - this(x, tree) - case _ => - super.foldOver(x, tree) - } - } - - /** Fold `f` over all tree nodes, in depth-first, prefix order */ - class UntypedDeepFolder[X](f: (X, Tree) => X) extends UntypedTreeAccumulator[X] { - def apply(x: X, tree: Tree)(implicit ctx: Context): X = foldOver(f(x, tree), tree) - } -} diff --git a/src/dotty/tools/dotc/config/CompilerCommand.scala b/src/dotty/tools/dotc/config/CompilerCommand.scala deleted file mode 100644 index 19ede3cec..000000000 --- a/src/dotty/tools/dotc/config/CompilerCommand.scala +++ /dev/null @@ -1,128 +0,0 @@ - -package dotty.tools.dotc -package config - -import java.io.File -import Settings._ -import core.Contexts._ -import util.DotClass -import Properties._ - -object CompilerCommand extends DotClass { - - /** The name of the command */ - def cmdName = "scalac" - - private def explainAdvanced = """ - |-- Notes on option parsing -- - |Boolean settings are always false unless set. - |Where multiple values are accepted, they should be comma-separated. - | example: -Xplugin:plugin1,plugin2 - |<phases> means one or a comma-separated list of: - | - (partial) phase names with an optional "+" suffix to include the next phase - | - the string "all" - | example: -Xprint:all prints all phases. - | example: -Xprint:front,mixin prints the frontend and mixin phases. - | example: -Ylog:erasure+ logs the erasure phase and the phase after the erasure phase. - | This is useful because during the tree transform of phase X, we often - | already are in phase X + 1. - """ - - def shortUsage = s"Usage: $cmdName <options> <source files>" - - def versionMsg = s"Dotty compiler $versionString -- $copyrightString" - - /** Distill arguments into summary detailing settings, errors and files to compiler */ - def distill(args: Array[String])(implicit ctx: Context): ArgsSummary = { - /** - * Expands all arguments starting with @ to the contents of the - * file named like each argument. - */ - def expandArg(arg: String): List[String] = unsupported("expandArg")/*{ - def stripComment(s: String) = s takeWhile (_ != '#') - val file = File(arg stripPrefix "@") - if (!file.exists) - throw new java.io.FileNotFoundException("argument file %s could not be found" format file.name) - - settings splitParams (file.lines() map stripComment mkString " ") - }*/ - - // expand out @filename to the contents of that filename - def expandedArguments = args.toList flatMap { - case x if x startsWith "@" => expandArg(x) - case x => List(x) - } - - ctx.settings.processArguments(expandedArguments, processAll = true) - } - - /** Provide usage feedback on argument summary, assuming that all settings - * are already applied in context. - * @return The list of files to compile. - */ - def checkUsage(summary: ArgsSummary, sourcesRequired: Boolean)(implicit ctx: Context): List[String] = { - val settings = ctx.settings - - /** Creates a help message for a subset of options based on cond */ - def availableOptionsMsg(cond: Setting[_] => Boolean): String = { - val ss = (ctx.settings.allSettings filter cond).toList sortBy (_.name) - val width = (ss map (_.name.length)).max - def format(s: String) = ("%-" + width + "s") format s - def helpStr(s: Setting[_]) = s"${format(s.name)} ${s.description}" - ss map helpStr mkString "\n" - } - - def createUsageMsg(label: String, shouldExplain: Boolean, cond: Setting[_] => Boolean): String = { - val prefix = List( - Some(shortUsage), - Some(explainAdvanced) filter (_ => shouldExplain), - Some(label + " options include:") - ).flatten mkString "\n" - - prefix + "\n" + availableOptionsMsg(cond) - } - - def isStandard(s: Setting[_]): Boolean = !isAdvanced(s) && !isPrivate(s) - def isAdvanced(s: Setting[_]): Boolean = s.name startsWith "-X" - def isPrivate(s: Setting[_]) : Boolean = s.name startsWith "-Y" - - /** Messages explaining usage and options */ - def usageMessage = createUsageMsg("where possible standard", shouldExplain = false, isStandard) - def xusageMessage = createUsageMsg("Possible advanced", shouldExplain = true, isAdvanced) - def yusageMessage = createUsageMsg("Possible private", shouldExplain = true, isPrivate) - - def shouldStopWithInfo = { - import settings._ - Set(help, Xhelp, Yhelp, showPlugins, showPhases) exists (_.value) - } - - def infoMessage: String = { - import settings._ - if (help.value) usageMessage - else if (Xhelp.value) xusageMessage - else if (Yhelp.value) yusageMessage -// else if (showPlugins.value) global.pluginDescriptions -// else if (showPhases.value) global.phaseDescriptions + ( -// if (debug.value) "\n" + global.phaseFlagDescriptions else "" -// ) - else "" - } - - if (summary.errors.nonEmpty) { - summary.errors foreach (ctx.error(_)) - ctx.echo(" dotc -help gives more information") - Nil - } - else if (settings.version.value) { - ctx.echo(versionMsg) - Nil - } - else if (shouldStopWithInfo) { - ctx.echo(infoMessage) - Nil - } else { - if (sourcesRequired && summary.arguments.isEmpty) ctx.echo(usageMessage) - summary.arguments - } - } -} diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala deleted file mode 100644 index 7744a5479..000000000 --- a/src/dotty/tools/dotc/config/Config.scala +++ /dev/null @@ -1,138 +0,0 @@ -package dotty.tools.dotc.config - -object Config { - - final val cacheMembersNamed = true - final val cacheAsSeenFrom = true - final val useFingerPrints = true // note: it currently seems to be slightly faster not to use them! my junit test: 548s without, 560s with. - final val cacheMemberNames = true - final val cacheImplicitScopes = true - - final val checkCacheMembersNamed = false - - /** When updating a constraint bound, check that the constrained parameter - * does not appear at the top-level of either of its bounds. - */ - final val checkConstraintsNonCyclic = false - - /** Make sure none of the bounds of a parameter in an OrderingConstraint - * contains this parameter at its toplevel (i.e. as an operand of a - * combination of &'s and |'s.). The check is performed each time a new bound - * is added to the constraint. - */ - final val checkConstraintsSeparated = false - - /** Check that each constraint resulting from a subtype test - * is satisfiable. - */ - final val checkConstraintsSatisfiable = false - - /** Check that each constraint is fully propagated. i.e. - * If P <: Q then the upper bound of P is a subtype of the upper bound of Q - * and the lower bound of Q is a subtype of the lower bound of P. - */ - final val checkConstraintsPropagated = false - - /** Check that constraints of globally committable typer states are closed. - * NOTE: When enabled, the check can cause CyclicReference errors because - * it traverses all elements of a type. Such failures were observed when - * compiling all of dotty together (source seems to be in GenBCode which - * accesses javac's settings.) - * - * It is recommended to turn this option on only when chasing down - * a PolyParam instantiation error. See comment in Types.TypeVar.instantiate. - */ - final val debugCheckConstraintsClosed = false - - /** Check that no type appearing as the info of a SymDenotation contains - * skolem types. - */ - final val checkNoSkolemsInInfo = false - - /** Type comparer will fail with an assert if the upper bound - * of a constrained parameter becomes Nothing. This should be turned - * on only for specific debugging as normally instantiation to Nothing - * is not an error consdition. - */ - final val failOnInstantiationToNothing = false - - /** Enable noDoubleDef checking if option "-YnoDoubleDefs" is set. - * The reason to have an option as well as the present global switch is - * that the noDoubleDef checking is done in a hotspot, and we do not - * want to incur the overhead of checking an option each time. - */ - final val checkNoDoubleBindings = true - - /** Check positions for consistency after parsing */ - final val checkPositions = true - - /** Show subtype traces for all deep subtype recursions */ - final val traceDeepSubTypeRecursions = false - - /** When explaining subtypes and this flag is set, also show the classes of the compared types. */ - final val verboseExplainSubtype = true - - /** If this flag is set, take the fast path when comparing same-named type-aliases and types */ - final val fastPathForRefinedSubtype = true - - /** If this flag is set, higher-kinded applications are checked for validity - */ - final val checkHKApplications = false - - /** The recursion depth for showing a summarized string */ - final val summarizeDepth = 2 - - /** Check that variances of lambda arguments match the - * variance of the underlying lambda class. - */ - final val checkLambdaVariance = false - - /** Check that certain types cannot be created in erasedTypes phases. - * Note: Turning this option on will get some false negatives, since it is - * possible that And/Or types are still created during erasure as the result - * of some operation on an existing type. - */ - final val checkUnerased = false - - /** In `derivedSelect`, rewrite - * - * (S & T)#A --> S#A & T#A - * (S | T)#A --> S#A | T#A - * - * Not sure whether this is useful. Preliminary measurements show a slowdown of about - * 7% for the build when this option is enabled. - */ - final val splitProjections = false - - /** If this flag is on, always rewrite an application `S[Ts]` where `S` is an alias for - * `[Xs] -> U` to `[Xs := Ts]U`. - * Turning this flag on was observed to give a ~6% speedup on the JUnit test suite. - */ - final val simplifyApplications = true - - /** Initial size of superId table */ - final val InitialSuperIdsSize = 4096 - - /** Initial capacity of uniques HashMap */ - final val initialUniquesCapacity = 40000 - - /** How many recursive calls to NamedType#underlying are performed before logging starts. */ - final val LogPendingUnderlyingThreshold = 50 - - /** How many recursive calls to isSubType are performed before logging starts. */ - final val LogPendingSubTypesThreshold = 50 - - /** How many recursive calls to findMember are performed before logging names starts - * Note: this threshold has to be chosen carefully. Too large, and programs - * like tests/pos/IterableSelfRec go into polynomial (or even exponential?) - * compile time slowdown. Too small and normal programs will cause the compiler to - * do inefficient operations on findMember. The current value is determined - * so that (1) IterableSelfRec still compiles in reasonable time (< 10sec) (2) Compiling - * dotty itself only causes small pending names lists to be generated (we measured - * at max 6 elements) and these lists are never searched with contains. - */ - final val LogPendingFindMemberThreshold = 10 - - /** Maximal number of outstanding recursive calls to findMember */ - final val PendingFindMemberLimit = LogPendingFindMemberThreshold * 4 -} diff --git a/src/dotty/tools/dotc/config/JavaPlatform.scala b/src/dotty/tools/dotc/config/JavaPlatform.scala deleted file mode 100644 index a695202d3..000000000 --- a/src/dotty/tools/dotc/config/JavaPlatform.scala +++ /dev/null @@ -1,70 +0,0 @@ -package dotty.tools -package dotc -package config - -import io.{AbstractFile,ClassPath,JavaClassPath,MergedClassPath,DeltaClassPath} -import ClassPath.{ JavaContext, DefaultJavaContext } -import core._ -import Symbols._, Types._, Contexts._, Denotations._, SymDenotations._, StdNames._, Names._ -import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._ -import transform.ExplicitOuter, transform.SymUtils._ - -class JavaPlatform extends Platform { - - private var currentClassPath: Option[MergedClassPath] = None - - def classPath(implicit ctx: Context): ClassPath = { - if (currentClassPath.isEmpty) - currentClassPath = Some(new PathResolver().result) - val cp = currentClassPath.get - //println(cp) - cp - } - - // The given symbol is a method with the right name and signature to be a runnable java program. - def isJavaMainMethod(sym: SymDenotation)(implicit ctx: Context) = - (sym.name == nme.main) && (sym.info match { - case t@MethodType(_, defn.ArrayOf(el) :: Nil) => el =:= defn.StringType && (t.resultType isRef defn.UnitClass) - case _ => false - }) - - // The given class has a main method. - def hasJavaMainMethod(sym: Symbol)(implicit ctx: Context): Boolean = - (sym.info member nme.main).hasAltWith { - case x: SymDenotation => isJavaMainMethod(x) - case _ => false - } - - /** Update classpath with a substituted subentry */ - def updateClassPath(subst: Map[ClassPath, ClassPath]) = - currentClassPath = Some(new DeltaClassPath(currentClassPath.get, subst)) - - def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = new ctx.base.loaders.PackageLoader(root, classPath) - - /** Is the SAMType `cls` also a SAM under the rules of the JVM? */ - def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean = - cls.is(NoInitsTrait) && - cls.superClass == defn.ObjectClass && - cls.directlyInheritedTraits.forall(_.is(NoInits)) && - !ExplicitOuter.needsOuterIfReferenced(cls) && - cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary - - /** We could get away with excluding BoxedBooleanClass for the - * purpose of equality testing since it need not compare equal - * to anything but other booleans, but it should be present in - * case this is put to other uses. - */ - def isMaybeBoxed(sym: ClassSymbol)(implicit ctx: Context) = { - val d = defn - import d._ - (sym == ObjectClass) || - (sym == JavaSerializableClass) || - (sym == ComparableClass) || - (sym derivesFrom BoxedNumberClass) || - (sym derivesFrom BoxedCharClass) || - (sym derivesFrom BoxedBooleanClass) - } - - def newClassLoader(bin: AbstractFile)(implicit ctx: Context): SymbolLoader = - new ClassfileLoader(bin) -} diff --git a/src/dotty/tools/dotc/config/OutputDirs.scala b/src/dotty/tools/dotc/config/OutputDirs.scala deleted file mode 100644 index a87eb9bce..000000000 --- a/src/dotty/tools/dotc/config/OutputDirs.scala +++ /dev/null @@ -1,116 +0,0 @@ -package dotty.tools -package dotc -package config - -import io._ - -/** A class for holding mappings from source directories to - * their output location. This functionality can be accessed - * only programmatically. The command line compiler uses a - * single output location, but tools may use this functionality - * to set output location per source directory. - */ -class OutputDirs { - /** Pairs of source directory - destination directory. */ - private var outputDirs: List[(AbstractFile, AbstractFile)] = Nil - - /** If this is not None, the output location where all - * classes should go. - */ - private var singleOutDir: Option[AbstractFile] = None - - /** Add a destination directory for sources found under srcdir. - * Both directories should exits. - */ - def add(srcDir: String, outDir: String): Unit = - add(checkDir(AbstractFile.getDirectory(srcDir), srcDir), - checkDir(AbstractFile.getDirectory(outDir), outDir)) - - /** Check that dir is exists and is a directory. */ - private def checkDir(dir: AbstractFile, name: String, allowJar: Boolean = false): AbstractFile = ( - if (dir != null && dir.isDirectory) - dir - // was: else if (allowJar && dir == null && Path.isJarOrZip(name, false)) - else if (allowJar && dir == null && Jar.isJarOrZip(name, false)) - new PlainFile(Path(name)) - else - throw new FatalError(name + " does not exist or is not a directory")) - - /** Set the single output directory. From now on, all files will - * be dumped in there, regardless of previous calls to 'add'. - */ - def setSingleOutput(outDir: String): Unit = { - val dst = AbstractFile.getDirectory(outDir) - setSingleOutput(checkDir(dst, outDir, true)) - } - - def getSingleOutput: Option[AbstractFile] = singleOutDir - - /** Set the single output directory. From now on, all files will - * be dumped in there, regardless of previous calls to 'add'. - */ - def setSingleOutput(dir: AbstractFile): Unit = { - singleOutDir = Some(dir) - } - - def add(src: AbstractFile, dst: AbstractFile): Unit = { - singleOutDir = None - outputDirs ::= ((src, dst)) - } - - /** Return the list of source-destination directory pairs. */ - def outputs: List[(AbstractFile, AbstractFile)] = outputDirs - - /** Return the output directory for the given file. - */ - def outputDirFor(src: AbstractFile): AbstractFile = { - def isBelow(srcDir: AbstractFile, outDir: AbstractFile) = - src.path.startsWith(srcDir.path) - - singleOutDir match { - case Some(d) => d - case None => - (outputs find (isBelow _).tupled) match { - case Some((_, d)) => d - case _ => - throw new FatalError("Could not find an output directory for " - + src.path + " in " + outputs) - } - } - } - - /** Return the source file path(s) which correspond to the given - * classfile path and SourceFile attribute value, subject to the - * condition that source files are arranged in the filesystem - * according to Java package layout conventions. - * - * The given classfile path must be contained in at least one of - * the specified output directories. If it does not then this - * method returns Nil. - * - * Note that the source file is not required to exist, so assuming - * a valid classfile path this method will always return a list - * containing at least one element. - * - * Also that if two or more source path elements target the same - * output directory there will be two or more candidate source file - * paths. - */ - def srcFilesFor(classFile: AbstractFile, srcPath: String): List[AbstractFile] = { - def isBelow(srcDir: AbstractFile, outDir: AbstractFile) = - classFile.path.startsWith(outDir.path) - - singleOutDir match { - case Some(d) => - d match { - case _: VirtualDirectory | _: io.ZipArchive => Nil - case _ => List(d.lookupPathUnchecked(srcPath, false)) - } - case None => - (outputs filter (isBelow _).tupled) match { - case Nil => Nil - case matches => matches.map(_._1.lookupPathUnchecked(srcPath, false)) - } - } - } -} diff --git a/src/dotty/tools/dotc/config/PathResolver.scala b/src/dotty/tools/dotc/config/PathResolver.scala deleted file mode 100644 index aa4d8aeb0..000000000 --- a/src/dotty/tools/dotc/config/PathResolver.scala +++ /dev/null @@ -1,281 +0,0 @@ -package dotty.tools -package dotc -package config - -import java.net.{ URL, MalformedURLException } -import WrappedProperties.AccessControl -import io.{ ClassPath, JavaClassPath, File, Directory, Path, AbstractFile } -import ClassPath.{ JavaContext, DefaultJavaContext, join, split } -import PartialFunction.condOpt -import scala.language.postfixOps -import core.Contexts._ -import Settings._ - -// Loosely based on the draft specification at: -// https://wiki.scala-lang.org/display/SW/Classpath - -object PathResolver { - - // Imports property/environment functions which suppress - // security exceptions. - import AccessControl._ - - def firstNonEmpty(xs: String*) = xs find (_ != "") getOrElse "" - - /** Map all classpath elements to absolute paths and reconstruct the classpath. - */ - def makeAbsolute(cp: String) = ClassPath.map(cp, x => Path(x).toAbsolute.path) - - /** pretty print class path */ - def ppcp(s: String) = split(s) match { - case Nil => "" - case Seq(x) => x - case xs => xs map ("\n" + _) mkString - } - - /** Values found solely by inspecting environment or property variables. - */ - object Environment { - private def searchForBootClasspath = ( - systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse "" - ) - - /** Environment variables which java pays attention to so it - * seems we do as well. - */ - def classPathEnv = envOrElse("CLASSPATH", "") - def sourcePathEnv = envOrElse("SOURCEPATH", "") - - def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath) - - def javaExtDirs = propOrEmpty("java.ext.dirs") - def scalaHome = propOrEmpty("scala.home") - def scalaExtDirs = propOrEmpty("scala.ext.dirs") - - /** The java classpath and whether to use it. */ - def javaUserClassPath = propOrElse("java.class.path", "") - def useJavaClassPath = propOrFalse("scala.usejavacp") - - override def toString = s""" - |object Environment { - | scalaHome = $scalaHome (useJavaClassPath = $useJavaClassPath) - | javaBootClassPath = <${javaBootClassPath.length} chars> - | javaExtDirs = ${ppcp(javaExtDirs)} - | javaUserClassPath = ${ppcp(javaUserClassPath)} - | scalaExtDirs = ${ppcp(scalaExtDirs)} - |}""".trim.stripMargin - } - - /** Default values based on those in Environment as interpreted according - * to the path resolution specification. - */ - object Defaults { - def scalaSourcePath = Environment.sourcePathEnv - def javaBootClassPath = Environment.javaBootClassPath - def javaUserClassPath = Environment.javaUserClassPath - def javaExtDirs = Environment.javaExtDirs - def useJavaClassPath = Environment.useJavaClassPath - - def scalaHome = Environment.scalaHome - def scalaHomeDir = Directory(scalaHome) - def scalaHomeExists = scalaHomeDir.isDirectory - def scalaLibDir = Directory(scalaHomeDir / "lib") - def scalaClassesDir = Directory(scalaHomeDir / "classes") - - def scalaLibAsJar = File(scalaLibDir / "scala-library.jar") - def scalaLibAsDir = Directory(scalaClassesDir / "library") - - def scalaLibDirFound: Option[Directory] = - if (scalaLibAsJar.isFile) Some(scalaLibDir) - else if (scalaLibAsDir.isDirectory) Some(scalaClassesDir) - else None - - def scalaLibFound = - if (scalaLibAsJar.isFile) scalaLibAsJar.path - else if (scalaLibAsDir.isDirectory) scalaLibAsDir.path - else "" - - // XXX It must be time for someone to figure out what all these things - // are intended to do. This is disabled here because it was causing all - // the scala jars to end up on the classpath twice: one on the boot - // classpath as set up by the runner (or regular classpath under -nobootcp) - // and then again here. - def scalaBootClassPath = "" - // scalaLibDirFound match { - // case Some(dir) if scalaHomeExists => - // val paths = ClassPath expandDir dir.path - // join(paths: _*) - // case _ => "" - // } - - def scalaExtDirs = Environment.scalaExtDirs - - def scalaPluginPath = (scalaHomeDir / "misc" / "scala-devel" / "plugins").path - - override def toString = """ - |object Defaults { - | scalaHome = %s - | javaBootClassPath = %s - | scalaLibDirFound = %s - | scalaLibFound = %s - | scalaBootClassPath = %s - | scalaPluginPath = %s - |}""".trim.stripMargin.format( - scalaHome, - ppcp(javaBootClassPath), - scalaLibDirFound, scalaLibFound, - ppcp(scalaBootClassPath), ppcp(scalaPluginPath) - ) - } - - def fromPathString(path: String)(implicit ctx: Context): JavaClassPath = { - val settings = ctx.settings.classpath.update(path) - new PathResolver()(ctx.fresh.setSettings(settings)).result - } - - /** With no arguments, show the interesting values in Environment and Defaults. - * If there are arguments, show those in Calculated as if those options had been - * given to a scala runner. - */ - def main(args: Array[String]): Unit = { - if (args.isEmpty) { - println(Environment) - println(Defaults) - } - else { - implicit val ctx: Context = (new ContextBase).initialCtx // Dotty deviation: implicits need explicit type - val ArgsSummary(sstate, rest, errors) = - ctx.settings.processArguments(args.toList, true) - errors.foreach(println) - val pr = new PathResolver()(ctx.fresh.setSettings(sstate)) - println(" COMMAND: 'scala %s'".format(args.mkString(" "))) - println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" "))) - pr.result.show - } - } -} -import PathResolver.{ Defaults, Environment, firstNonEmpty, ppcp } - -class PathResolver(implicit ctx: Context) { - import ctx.base.settings - - val context = ClassPath.DefaultJavaContext - - private def cmdLineOrElse(name: String, alt: String) = { - (commandLineFor(name) match { - case Some("") => None - case x => x - }) getOrElse alt - } - - private def commandLineFor(s: String): Option[String] = condOpt(s) { - case "javabootclasspath" => settings.javabootclasspath.value - case "javaextdirs" => settings.javaextdirs.value - case "bootclasspath" => settings.bootclasspath.value - case "extdirs" => settings.extdirs.value - case "classpath" | "cp" => settings.classpath.value - case "sourcepath" => settings.sourcepath.value - case "priorityclasspath" => settings.priorityclasspath.value - } - - /** Calculated values based on any given command line options, falling back on - * those in Defaults. - */ - object Calculated { - def scalaHome = Defaults.scalaHome - def useJavaClassPath = settings.usejavacp.value || Defaults.useJavaClassPath - def javaBootClassPath = cmdLineOrElse("javabootclasspath", Defaults.javaBootClassPath) - def javaExtDirs = cmdLineOrElse("javaextdirs", Defaults.javaExtDirs) - def javaUserClassPath = if (useJavaClassPath) Defaults.javaUserClassPath else "" - def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath) - def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs) - def priorityClassPath = cmdLineOrElse("priorityclasspath", "") - /** Scaladoc doesn't need any bootstrapping, otherwise will create errors such as: - * [scaladoc] ../scala-trunk/src/reflect/scala/reflect/macros/Reifiers.scala:89: error: object api is not a member of package reflect - * [scaladoc] case class ReificationException(val pos: reflect.api.PositionApi, val msg: String) extends Throwable(msg) - * [scaladoc] ^ - * because the bootstrapping will look at the sourcepath and create package "reflect" in "<root>" - * and then when typing relative names, instead of picking <root>.scala.relect, typedIdentifier will pick up the - * <root>.reflect package created by the bootstrapping. Thus, no bootstrapping for scaladoc! - * TODO: we should refactor this as a separate -bootstrap option to have a clean implementation, no? */ - def sourcePath = cmdLineOrElse("sourcepath", Defaults.scalaSourcePath) - - /** Against my better judgment, giving in to martin here and allowing - * CLASSPATH to be used automatically. So for the user-specified part - * of the classpath: - * - * - If -classpath or -cp is given, it is that - * - Otherwise, if CLASSPATH is set, it is that - * - If neither of those, then "." is used. - */ - def userClassPath = { - if (!settings.classpath.isDefault) - settings.classpath.value - else sys.env.getOrElse("CLASSPATH", ".") - } - - import context._ - - // Assemble the elements! - // priority class path takes precedence - def basis = List[Traversable[ClassPath]]( - classesInExpandedPath(priorityClassPath), // 0. The priority class path (for testing). - classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. - contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. - classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. - classesInPath(scalaBootClassPath), // 4. The Scala boot class path. - contentsOfDirsInPath(scalaExtDirs), // 5. The Scala extension class path. - classesInExpandedPath(userClassPath), // 6. The Scala application class path. - sourcesInPath(sourcePath) // 7. The Scala source path. - ) - - lazy val containers = basis.flatten.distinct - - override def toString = """ - |object Calculated { - | scalaHome = %s - | priorityClassPath = %s - | javaBootClassPath = %s - | javaExtDirs = %s - | javaUserClassPath = %s - | useJavaClassPath = %s - | scalaBootClassPath = %s - | scalaExtDirs = %s - | userClassPath = %s - | sourcePath = %s - |}""".trim.stripMargin.format( - scalaHome, ppcp(priorityClassPath), - ppcp(javaBootClassPath), ppcp(javaExtDirs), ppcp(javaUserClassPath), - useJavaClassPath, - ppcp(scalaBootClassPath), ppcp(scalaExtDirs), ppcp(userClassPath), - ppcp(sourcePath) - ) - } - - def containers = Calculated.containers - - lazy val result: JavaClassPath = { - // Prioritize `dotty.jar` and `dotty-lib.jar` to shadow others - val (dottyJars, others) = - containers.partition(x => x.name.contains("dotty-lib.jar") || x.name.contains("dotty.jar")) - // Then any jars with `dotty` in the name - putting them before scala-library - val (dottyCp, remaining) = - others.partition(_.name.contains("dotty-")) - - val cp = new JavaClassPath((dottyJars ++ dottyCp ++ remaining).toIndexedSeq, context) - - if (settings.Ylogcp.value) { - Console.println("Classpath built from " + settings.toConciseString(ctx.sstate)) - Console.println("Defaults: " + PathResolver.Defaults) - Console.println("Calculated: " + Calculated) - - val xs = (Calculated.basis drop 2).flatten.distinct - println("After java boot/extdirs classpath has %d entries:" format xs.size) - xs foreach (x => println(" " + x)) - } - cp - } - - def asURLs = result.asURLs - -} diff --git a/src/dotty/tools/dotc/config/Platform.scala b/src/dotty/tools/dotc/config/Platform.scala deleted file mode 100644 index 062d9002d..000000000 --- a/src/dotty/tools/dotc/config/Platform.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Paul Phillips - */ - -package dotty.tools -package dotc -package config - -import io.{ClassPath, AbstractFile} -import core.Contexts._, core.Symbols._ -import core.SymbolLoader - -/** The platform dependent pieces of Global. - */ -abstract class Platform { - - /** The root symbol loader. */ - def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader - - /** The compiler classpath. */ - def classPath(implicit ctx: Context): ClassPath - - /** Update classpath with a substitution that maps entries to entries */ - def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit - - /** Any platform-specific phases. */ - //def platformPhases: List[SubComponent] - - /** Is the SAMType `cls` also a SAM under the rules of the platform? */ - def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean - - /** The various ways a boxed primitive might materialize at runtime. */ - def isMaybeBoxed(sym: ClassSymbol)(implicit ctx: Context): Boolean - - /** Create a new class loader to load class file `bin` */ - def newClassLoader(bin: AbstractFile)(implicit ctx: Context): SymbolLoader -} - diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala deleted file mode 100644 index 002d0f933..000000000 --- a/src/dotty/tools/dotc/config/Printers.scala +++ /dev/null @@ -1,34 +0,0 @@ -package dotty.tools.dotc.config - -object Printers { - - class Printer { - def println(msg: => String): Unit = System.out.println(msg) - } - - object noPrinter extends Printer { - override def println(msg: => String): Unit = () - } - - val default: Printer = new Printer - val dottydoc: Printer = noPrinter - val core: Printer = noPrinter - val typr: Printer = noPrinter - val constr: Printer = noPrinter - val checks: Printer = noPrinter - val overload: Printer = noPrinter - val implicits: Printer = noPrinter - val implicitsDetailed: Printer = noPrinter - val subtyping: Printer = noPrinter - val unapp: Printer = noPrinter - val gadts: Printer = noPrinter - val hk: Printer = noPrinter - val variances: Printer = noPrinter - val incremental: Printer = noPrinter - val config: Printer = noPrinter - val transforms: Printer = noPrinter - val completions: Printer = noPrinter - val cyclicErrors: Printer = noPrinter - val pickling: Printer = noPrinter - val inlining: Printer = noPrinter -} diff --git a/src/dotty/tools/dotc/config/Properties.scala b/src/dotty/tools/dotc/config/Properties.scala deleted file mode 100644 index ec1f24d06..000000000 --- a/src/dotty/tools/dotc/config/Properties.scala +++ /dev/null @@ -1,165 +0,0 @@ -package dotty.tools -package dotc -package config - -import java.io.{ IOException, PrintWriter } -import java.util.jar.Attributes.{ Name => AttributeName } - -/** Loads `library.properties` from the jar. */ -object Properties extends PropertiesTrait { - protected def propCategory = "library" - protected def pickJarBasedOn = classOf[Option[_]] - - /** Scala manifest attributes. - */ - @sharable val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") -} - -trait PropertiesTrait { - protected def propCategory: String // specializes the remainder of the values - protected def pickJarBasedOn: Class[_] // props file comes from jar containing this - - /** The name of the properties file */ - protected val propFilename = "/" + propCategory + ".properties" - - /** The loaded properties */ - @sharable protected lazy val scalaProps: java.util.Properties = { - val props = new java.util.Properties - val stream = pickJarBasedOn getResourceAsStream propFilename - if (stream ne null) - quietlyDispose(props load stream, stream.close) - - props - } - - private def quietlyDispose(action: => Unit, disposal: => Unit) = - try { action } - finally { - try { disposal } - catch { case _: IOException => } - } - - def propIsSet(name: String) = System.getProperty(name) != null - def propIsSetTo(name: String, value: String) = propOrNull(name) == value - def propOrElse(name: String, alt: String) = System.getProperty(name, alt) - def propOrEmpty(name: String) = propOrElse(name, "") - def propOrNull(name: String) = propOrElse(name, null) - def propOrNone(name: String) = Option(propOrNull(name)) - def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) - def setProp(name: String, value: String) = System.setProperty(name, value) - def clearProp(name: String) = System.clearProperty(name) - - def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt - def envOrNone(name: String) = Option(System getenv name) - - // for values based on propFilename - def scalaPropOrElse(name: String, alt: String): String = scalaProps.getProperty(name, alt) - def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") - def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)) - - /** The numeric portion of the runtime Scala version, if this is a final - * release. If for instance the versionString says "version 2.9.0.final", - * this would return Some("2.9.0"). - * - * @return Some(version) if this is a final release build, None if - * it is an RC, Beta, etc. or was built from source, or if the version - * cannot be read. - */ - val releaseVersion = - for { - v <- scalaPropOrNone("maven.version.number") - if !(v endsWith "-SNAPSHOT") - } yield v - - /** The development Scala version, if this is not a final release. - * The precise contents are not guaranteed, but it aims to provide a - * unique repository identifier (currently the svn revision) in the - * fourth dotted segment if the running version was built from source. - * - * @return Some(version) if this is a non-final version, None if this - * is a final release or the version cannot be read. - */ - val developmentVersion = - for { - v <- scalaPropOrNone("maven.version.number") - if v endsWith "-SNAPSHOT" - ov <- scalaPropOrNone("version.number") - } yield ov - - /** Either the development or release version if known, otherwise - * the empty string. - */ - def versionNumberString = scalaPropOrEmpty("version.number") - - /** The version number of the jar this was loaded from plus "version " prefix, - * or "version (unknown)" if it cannot be determined. - */ - val versionString = "version " + "0.01" //scalaPropOrElse("version.number", "(unknown)")" + - val copyrightString = "(c) 2013 LAMP/EPFL" // scalaPropOrElse("copyright.string", "(c) 2002-2011 LAMP/EPFL") - - /** This is the encoding to use reading in source files, overridden with -encoding - * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. - */ - def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") - def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") - - /** This is the default text encoding, overridden (unreliably) with - * `JAVA_OPTS="-Dfile.encoding=Foo"` - */ - def encodingString = propOrElse("file.encoding", "UTF-8") - - /** The default end of line character. - */ - def lineSeparator = propOrElse("line.separator", "\n") - - /** Various well-known properties. - */ - def javaClassPath = propOrEmpty("java.class.path") - def javaHome = propOrEmpty("java.home") - def javaVendor = propOrEmpty("java.vendor") - def javaVersion = propOrEmpty("java.version") - def javaVmInfo = propOrEmpty("java.vm.info") - def javaVmName = propOrEmpty("java.vm.name") - def javaVmVendor = propOrEmpty("java.vm.vendor") - def javaVmVersion = propOrEmpty("java.vm.version") - def osName = propOrEmpty("os.name") - def scalaHome = propOrEmpty("scala.home") - def tmpDir = propOrEmpty("java.io.tmpdir") - def userDir = propOrEmpty("user.dir") - def userHome = propOrEmpty("user.home") - def userName = propOrEmpty("user.name") - - /** Some derived values. - */ - def isWin = osName startsWith "Windows" - def isMac = javaVendor startsWith "Apple" - - // This is looking for javac, tools.jar, etc. - // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, - // and finally the system property based javaHome. - def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) - - def versionMsg = "Scala %s %s -- %s".format(propCategory, versionString, copyrightString) - def scalaCmd = if (isWin) "scala.bat" else "scala" - def scalacCmd = if (isWin) "scalac.bat" else "scalac" - - /** Can the java version be determined to be at least as high as the argument? - * Hard to properly future proof this but at the rate 1.7 is going we can leave - * the issue for our cyborg grandchildren to solve. - */ - def isJavaAtLeast(version: String) = { - val okVersions = version match { - case "1.5" => List("1.5", "1.6", "1.7") - case "1.6" => List("1.6", "1.7") - case "1.7" => List("1.7") - case _ => Nil - } - okVersions exists (javaVersion startsWith _) - } - - // provide a main method so version info can be obtained by running this - def main(args: Array[String]): Unit = { - val writer = new PrintWriter(Console.err, true) - writer println versionMsg - } -} diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala deleted file mode 100644 index fd2ded0b5..000000000 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ /dev/null @@ -1,267 +0,0 @@ -package dotty.tools.dotc -package config - -import PathResolver.Defaults -import rewrite.Rewrites - -class ScalaSettings extends Settings.SettingGroup { - - protected def defaultClasspath = sys.env.getOrElse("CLASSPATH", ".") - - /** Path related settings. - */ - val bootclasspath = PathSetting("-bootclasspath", "Override location of bootstrap class files.", Defaults.scalaBootClassPath) - val extdirs = PathSetting("-extdirs", "Override location of installed extensions.", Defaults.scalaExtDirs) - val javabootclasspath = PathSetting("-javabootclasspath", "Override java boot classpath.", Defaults.javaBootClassPath) - val javaextdirs = PathSetting("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs) - val sourcepath = PathSetting("-sourcepath", "Specify location(s) of source files.", "") // Defaults.scalaSourcePath - val argfiles = BooleanSetting("@<file>", "A text file containing compiler arguments (options and source files)") - val classpath = PathSetting("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp" - val d = StringSetting("-d", "directory|jar", "destination for generated classfiles.", ".") - val priorityclasspath = PathSetting("-priorityclasspath", "class path that takes precedence over all other paths (or testing only)", "") - - /** Other settings. - */ - val dependencyfile = StringSetting("-dependencyfile", "file", "Set dependency tracking file.", ".scala_dependencies") - val deprecation = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.") - val migration = BooleanSetting("-migration", "Emit warning and location for migration issues from Scala 2.") - val encoding = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding) - val explaintypes = BooleanSetting("-explaintypes", "Explain type errors in more detail.") - val explain = BooleanSetting("-explain", "Explain errors in more detail.") - val feature = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.") - val g = ChoiceSetting("-g", "level", "Set level of generated debugging info.", List("none", "source", "line", "vars", "notailcalls"), "vars") - val help = BooleanSetting("-help", "Print a synopsis of standard options") - val nowarn = BooleanSetting("-nowarn", "Generate no warnings.") - val color = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/) - val target = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", - List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "jvm-1.8", "msil"), - "jvm-1.8") - val scalajs = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).") - val unchecked = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.") - val uniqid = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.") - val usejavacp = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.") - val verbose = BooleanSetting("-verbose", "Output messages about what the compiler is doing.") - val version = BooleanSetting("-version", "Print product version and exit.") - val pageWidth = IntSetting("-pagewidth", "Set page width", 80) - - val jvmargs = PrefixSetting("-J<flag>", "-J", "Pass <flag> directly to the runtime system.") - val defines = PrefixSetting("-Dproperty=value", "-D", "Pass -Dproperty=value directly to the runtime system.") - val toolcp = PathSetting("-toolcp", "Add to the runner classpath.", "") - val nobootcp = BooleanSetting("-nobootcp", "Do not use the boot classpath for the scala jars.") - val strict = BooleanSetting("-strict", "Use strict type rules, which means some formerly legal code does not typecheck anymore.") - - val nospecialization = BooleanSetting("-no-specialization", "Ignore @specialize annotations.") - val language = MultiStringSetting("-language", "feature", "Enable one or more language features.") - val rewrite = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax") - - /** -X "Advanced" settings - */ - val Xhelp = BooleanSetting("-X", "Print a synopsis of advanced options.") - val assemname = StringSetting("-Xassem-name", "file", "(Requires -target:msil) Name of the output assembly.", "").dependsOn(target, "msil") - val assemrefs = StringSetting("-Xassem-path", "path", "(Requires -target:msil) List of assemblies referenced by the program.", ".").dependsOn(target, "msil") - val assemextdirs = StringSetting("-Xassem-extdirs", "dirs", "(Requires -target:msil) List of directories containing assemblies. default:lib", Defaults.scalaLibDir.path).dependsOn(target, "msil") - val sourcedir = StringSetting("-Xsourcedir", "directory", "(Requires -target:msil) Mirror source folder structure in output directory.", ".").dependsOn(target, "msil") - val checkInit = BooleanSetting("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") - val noassertions = BooleanSetting("-Xdisable-assertions", "Generate no assertions or assumptions.") -// val elidebelow = IntSetting("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument", -// elidable.MINIMUM, None, elidable.byName get _) - val noForwarders = BooleanSetting("-Xno-forwarders", "Do not generate static forwarders in mirror classes.") - val genPhaseGraph = StringSetting("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot.", "") - val XlogImplicits = BooleanSetting("-Xlog-implicits", "Show more detail on why some implicits are not applicable.") - val XminImplicitSearchDepth = IntSetting("-Xmin-implicit-search-depth", "Set number of levels of implicit searches undertaken before checking for divergence.", 5) - val xmaxInlines = IntSetting("-Xmax-inlines", "Maximal number of successive inlines", 70) - val logImplicitConv = BooleanSetting("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.") - val logReflectiveCalls = BooleanSetting("-Xlog-reflective-calls", "Print a message when a reflective method call is generated") - val logFreeTerms = BooleanSetting("-Xlog-free-terms", "Print a message when reification creates a free term.") - val logFreeTypes = BooleanSetting("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") - val maxClassfileName = IntSetting("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, 72 to 255) - val Xmigration = VersionSetting("-Xmigration", "Warn about constructs whose behavior may have changed since version.") - val Xsource = VersionSetting("-Xsource", "Treat compiler input as Scala source for the specified version.") - val Xverify = BooleanSetting("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") - val plugin = MultiStringSetting("-Xplugin", "file", "Load one or more plugins from files.") - val disable = MultiStringSetting("-Xplugin-disable", "plugin", "Disable the given plugin(s).") - val showPlugins = BooleanSetting("-Xplugin-list", "Print a synopsis of loaded plugins.") - val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort unless the given plugin(s) are available.") - val pluginsDir = StringSetting("-Xpluginsdir", "path", "Path to search compiler plugins.", Defaults.scalaPluginPath) - val Xprint = PhasesSetting("-Xprint", "Print out program after") - val writeICode = PhasesSetting("-Xprint-icode", "Log internal icode to *.icode files after", "icode") - val Xprintpos = BooleanSetting("-Xprint-pos", "Print tree positions, as offsets.") - val printtypes = BooleanSetting("-Xprint-types", "Print tree types (debugging option).") - val XprintDiff = BooleanSetting("-Xprint-diff", "Print changed parts of the tree since last print.") - val XprintDiffDel = BooleanSetting("-Xprint-diff-del", "Print chaged parts of the tree since last print including deleted parts.") - val prompt = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).") - val script = StringSetting("-Xscript", "object", "Treat the source file as a script and wrap it in a main method.", "") - val mainClass = StringSetting("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "") - val Xshowcls = StringSetting("-Xshow-class", "class", "Show internal representation of class.", "") - val Xshowobj = StringSetting("-Xshow-object", "object", "Show internal representation of object.", "") - val showPhases = BooleanSetting("-Xshow-phases", "Print a synopsis of compiler phases.") - val sourceReader = StringSetting("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "") - val XnoValueClasses = BooleanSetting("-Xno-value-classes", "Do not use value classes. Helps debugging.") - val XreplLineWidth = IntSetting("-Xrepl-line-width", "Maximial number of columns per line for REPL output", 390) - val XoldPatmat = BooleanSetting("-Xoldpatmat", "Use the pre-2.10 pattern matcher. Otherwise, the 'virtualizing' pattern matcher is used in 2.10.") - val XnoPatmatAnalysis = BooleanSetting("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") - val XfullLubs = BooleanSetting("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") - - /** -Y "Private" settings - */ - val overrideObjects = BooleanSetting("-Yoverride-objects", "Allow member objects to be overridden.") - val overrideVars = BooleanSetting("-Yoverride-vars", "Allow vars to be overridden.") - val Yhelp = BooleanSetting("-Y", "Print a synopsis of private options.") - val browse = PhasesSetting("-Ybrowse", "Browse the abstract syntax tree after") - val Ycheck = PhasesSetting("-Ycheck", "Check the tree at the end of") - val YcheckMods = BooleanSetting("-Ycheck-mods", "Check that symbols and their defining trees have modifiers in sync") - val YcheckTypedTrees = BooleanSetting("-YcheckTypedTrees", "Check all constructured typed trees for type correctness") - val Yshow = PhasesSetting("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after") - val Ycloselim = BooleanSetting("-Yclosure-elim", "Perform closure elimination.") - val Ycompacttrees = BooleanSetting("-Ycompact-trees", "Use compact tree printer when displaying trees.") - val noCompletion = BooleanSetting("-Yno-completion", "Disable tab-completion in the REPL.") - val Ydce = BooleanSetting("-Ydead-code", "Perform dead code elimination.") - val debug = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.") - val debugNames = BooleanSetting("-YdebugNames", "Show name-space indicators when printing names") - val debugTrace = BooleanSetting("-Ydebug-trace", "Trace core operations") - val debugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions") - val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)") - //val doc = BooleanSetting ("-Ydoc", "Generate documentation") - val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") - val inlineHandlers = BooleanSetting("-Yinline-handlers", "Perform exception handler inlining when possible.") - val YinlinerWarnings = BooleanSetting("-Yinline-warnings", "Emit inlining warnings. (Normally surpressed due to high volume)") - val Ylinearizer = ChoiceSetting("-Ylinearizer", "which", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo") - val log = PhasesSetting("-Ylog", "Log operations during") - val Ylogcp = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.") - val Ynogenericsig = BooleanSetting("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.") - val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") - val YnoPredef = BooleanSetting("-Yno-predef", "Compile without importing Predef.") - val noAdaptedArgs = BooleanSetting("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.") - val selfInAnnots = BooleanSetting("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.") - val Yshowtrees = BooleanSetting("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs in formatted form.") - val YshowtreesCompact = BooleanSetting("-Yshow-trees-compact", "(Requires -Xprint:) Print detailed ASTs in compact form.") - val YshowtreesStringified = BooleanSetting("-Yshow-trees-stringified", "(Requires -Xprint:) Print stringifications along with detailed ASTs.") - val Yshowsyms = BooleanSetting("-Yshow-syms", "Print the AST symbol hierarchy after each phase.") - val Yshowsymkinds = BooleanSetting("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") - val Yskip = PhasesSetting("-Yskip", "Skip") - val Ygenjavap = StringSetting("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") - val Ydumpclasses = StringSetting("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") - val Ynosqueeze = BooleanSetting("-Yno-squeeze", "Disable creation of compact code in matching.") - val YstopAfter = PhasesSetting("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat - val YstopBefore = PhasesSetting("-Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully - val refinementMethodDispatch = ChoiceSetting("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") - val Yrangepos = BooleanSetting("-Yrangepos", "Use range positions for syntax trees.") - val Ybuilderdebug = ChoiceSetting("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none") - val Yreifycopypaste = BooleanSetting("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") - val Yreplsync = BooleanSetting("-Yrepl-sync", "Do not use asynchronous code for repl startup") - val YmethodInfer = BooleanSetting("-Yinfer-argument-types", "Infer types for arguments of overriden methods.") - val etaExpandKeepsStar = BooleanSetting("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.") - val Yinvalidate = StringSetting("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "") - val noSelfCheck = BooleanSetting("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.") - val YtraceContextCreation = BooleanSetting("-Ytrace-context-creation", "Store stack trace of context creations.") - val YshowSuppressedErrors = BooleanSetting("-Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally supressed.") - val Yheartbeat = BooleanSetting("-Yheartbeat", "show heartbeat stack trace of compiler operations.") - val Yprintpos = BooleanSetting("-Yprintpos", "show tree positions.") - val YnoDeepSubtypes = BooleanSetting("-Yno-deep-subtypes", "throw an exception on deep subtyping call stacks.") - val YplainPrinter = BooleanSetting("-Yplain-printer", "Pretty-print using a plain printer.") - val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.") - val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler") - val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.") - val YkeepComments = BooleanSetting("-Ykeep-comments", "Keep comments when scanning source files.") - val YforceSbtPhases = BooleanSetting("-Yforce-sbt-phases", "Run the phases used by sbt for incremental compilation (ExtractDependencies and ExtractAPI) even if the compiler is ran outside of sbt, for debugging.") - val YdumpSbtInc = BooleanSetting("-Ydump-sbt-inc", "For every compiled foo.scala, output the API representation and dependencies used for sbt incremental compilation in foo.inc, implies -Yforce-sbt-phases.") - val YcheckAllPatmat = BooleanSetting("-Ycheck-all-patmat", "Check exhaustivity and redundancy of all pattern matching (used for testing the algorithm)") - def stop = YstopAfter - - /** Area-specific debug output. - */ - val Ybuildmanagerdebug = BooleanSetting("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.") - val Ycompletion = BooleanSetting("-Ycompletion-debug", "Trace all tab completion activity.") - val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.") - val Yidedebug = BooleanSetting("-Yide-debug", "Generate, validate and output trees using the interactive compiler.") - val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") - val Yissuedebug = BooleanSetting("-Yissue-debug", "Print stack traces when a context issues an error.") - val YmacrodebugLite = BooleanSetting("-Ymacro-debug-lite", "Trace essential macro-related activities.") - val YmacrodebugVerbose = BooleanSetting("-Ymacro-debug-verbose", "Trace all macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.") - val Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.") - val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.") - val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.") - val Yrepldebug = BooleanSetting("-Yrepl-debug", "Trace all repl activity.") - val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.") - val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.") - val Yexplainlowlevel = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.") - val YnoDoubleBindings = BooleanSetting("-Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).") - val YshowVarBounds = BooleanSetting("-Yshow-var-bounds", "Print type variables with their bounds") - val YnoInline = BooleanSetting("-Yno-inline", "Suppress inlining.") - - val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize" - - /** IDE-specific settings - */ - val YpresentationVerbose = BooleanSetting("-Ypresentation-verbose", "Print information about presentation compiler tasks.") - val YpresentationDebug = BooleanSetting("-Ypresentation-debug", "Enable debugging output for the presentation compiler.") - val YpresentationStrict = BooleanSetting("-Ypresentation-strict", "Do not report type errors in sources with syntax errors.") - - val YpresentationLog = StringSetting("-Ypresentation-log", "file", "Log presentation compiler events into file", "") - val YpresentationReplay = StringSetting("-Ypresentation-replay", "file", "Replay presentation compiler events from file", "") - val YpresentationDelay = IntSetting("-Ypresentation-delay", "Wait number of ms after typing before starting typechecking", 0, 0 to 999) - - /** Doc specific settings */ - val template = OptionSetting[String]( - "-template", - "A mustache template for rendering each top-level entity in the API" - ) - - val resources = OptionSetting[String]( - "-resources", - "A directory containing static resources needed for the API documentation" - ) - - val DocTitle = StringSetting ( - "-Ydoc-title", - "title", - "The overall name of the Scaladoc site", - "" - ) - - val DocVersion = StringSetting ( - "-Ydoc-version", - "version", - "An optional version number, to be appended to the title", - "" - ) - - val DocOutput = StringSetting ( - "-Ydoc-output", - "outdir", - "The output directory in which to place the documentation", - "." - ) - - val DocFooter = StringSetting ( - "-Ydoc-footer", - "footer", - "A footer on every Scaladoc page, by default the EPFL/Lightbend copyright notice. Can be overridden with a custom footer.", - "" - ) - - val DocUncompilable = StringSetting ( - "-Ydoc-no-compile", - "path", - "A directory containing sources which should be parsed, no more (e.g. AnyRef.scala)", - "" - ) - - //def DocUncompilableFiles(implicit ctx: Context) = DocUncompilable.value match { - // case "" => Nil - // case path => io.Directory(path).deepFiles.filter(_ hasExtension "scala").toList - //} - - val DocExternalDoc = MultiStringSetting ( - "-Ydoc-external-doc", - "external-doc", - "comma-separated list of classpath_entry_path#doc_URL pairs describing external dependencies." - ) - - val DocAuthor = BooleanSetting("-Ydoc-author", "Include authors.", true) - - val DocGroups = BooleanSetting ( - "-Ydoc:groups", - "Group similar functions together (based on the @group annotation)" - ) -} diff --git a/src/dotty/tools/dotc/config/ScalaVersion.scala b/src/dotty/tools/dotc/config/ScalaVersion.scala deleted file mode 100644 index 02ba74af9..000000000 --- a/src/dotty/tools/dotc/config/ScalaVersion.scala +++ /dev/null @@ -1,184 +0,0 @@ -/* @author James Iry - */ -package dotty.tools -package dotc.config - -import scala.util.{Try, Success, Failure} - -/** - * Represents a single Scala version in a manner that - * supports easy comparison and sorting. - */ -sealed abstract class ScalaVersion extends Ordered[ScalaVersion] { - def unparse: String -} - -/** - * A scala version that sorts higher than all actual versions - */ -@sharable case object NoScalaVersion extends ScalaVersion { - def unparse = "none" - - def compare(that: ScalaVersion): Int = that match { - case NoScalaVersion => 0 - case _ => 1 - } -} - -/** - * A specific Scala version, not one of the magic min/max versions. An SpecificScalaVersion - * may or may not be a released version - i.e. this same class is used to represent - * final, release candidate, milestone, and development builds. The build argument is used - * to segregate builds - */ -case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion { - def unparse = s"${major}.${minor}.${rev}.${build.unparse}" - - def compare(that: ScalaVersion): Int = that match { - case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) => - // this could be done more cleanly by importing scala.math.Ordering.Implicits, but we have to do these - // comparisons a lot so I'm using brute force direct style code - if (major < thatMajor) -1 - else if (major > thatMajor) 1 - else if (minor < thatMinor) -1 - else if (minor > thatMinor) 1 - else if (rev < thatRev) -1 - else if (rev > thatRev) 1 - else build compare thatBuild - case AnyScalaVersion => 1 - case NoScalaVersion => -1 - } -} - -/** - * A Scala version that sorts lower than all actual versions - */ -@sharable case object AnyScalaVersion extends ScalaVersion { - def unparse = "any" - - def compare(that: ScalaVersion): Int = that match { - case AnyScalaVersion => 0 - case _ => -1 - } -} - -/** - * Methods for parsing ScalaVersions - */ -@sharable object ScalaVersion { - private val dot = "\\." - private val dash = "\\-" - private def not(s:String) = s"[^${s}]" - private val R = s"((${not(dot)}*)(${dot}(${not(dot)}*)(${dot}(${not(dash)}*)(${dash}(.*))?)?)?)".r - - def parse(versionString : String): Try[ScalaVersion] = { - def failure = Failure(new NumberFormatException( - s"There was a problem parsing ${versionString}. " + - "Versions should be in the form major[.minor[.revision]] " + - "where each part is a positive number, as in 2.10.1. " + - "The minor and revision parts are optional." - )) - - def toInt(s: String) = s match { - case null | "" => 0 - case _ => s.toInt - } - - def isInt(s: String) = Try(toInt(s)).isSuccess - - import ScalaBuild._ - - def toBuild(s: String) = s match { - case null | "FINAL" => Final - case s if (s.toUpperCase.startsWith("RC") && isInt(s.substring(2))) => RC(toInt(s.substring(2))) - case s if (s.toUpperCase.startsWith("M") && isInt(s.substring(1))) => Milestone(toInt(s.substring(1))) - case _ => Development(s) - } - - try versionString match { - case "" | "any" => Success(AnyScalaVersion) - case "none" => Success(NoScalaVersion) - case R(_, majorS, _, minorS, _, revS, _, buildS) => - Success(SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS))) - case _ => failure - } catch { - case e: NumberFormatException => failure - } - } - - /** - * The version of the compiler running now - */ - val current = parse(util.Properties.versionNumberString).get -} - -/** - * Represents the data after the dash in major.minor.rev-build - */ -abstract class ScalaBuild extends Ordered[ScalaBuild] { - /** - * Return a version of this build information that can be parsed back into the - * same ScalaBuild - */ - def unparse: String -} - -object ScalaBuild { - - /** A development, test, nightly, snapshot or other "unofficial" build - */ - case class Development(id: String) extends ScalaBuild { - def unparse = s"-${id}" - - def compare(that: ScalaBuild) = that match { - // sorting two development builds based on id is reasonably valid for two versions created with the same schema - // otherwise it's not correct, but since it's impossible to put a total ordering on development build versions - // this is a pragmatic compromise - case Development(thatId) => id compare thatId - // assume a development build is newer than anything else, that's not really true, but good luck - // mapping development build versions to other build types - case _ => 1 - } - } - - /** A final build - */ - case object Final extends ScalaBuild { - def unparse = "" - - def compare(that: ScalaBuild) = that match { - case Final => 0 - // a final is newer than anything other than a development build or another final - case Development(_) => -1 - case _ => 1 - } - } - - /** A candidate for final release - */ - case class RC(n: Int) extends ScalaBuild { - def unparse = s"-RC${n}" - - def compare(that: ScalaBuild) = that match { - // compare two rcs based on their RC numbers - case RC(thatN) => n - thatN - // an rc is older than anything other than a milestone or another rc - case Milestone(_) => 1 - case _ => -1 - } - } - - /** An intermediate release - */ - case class Milestone(n: Int) extends ScalaBuild { - def unparse = s"-M${n}" - - def compare(that: ScalaBuild) = that match { - // compare two milestones based on their milestone numbers - case Milestone(thatN) => n - thatN - // a milestone is older than anything other than another milestone - case _ => -1 - - } - } -} diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala deleted file mode 100644 index cffa047fe..000000000 --- a/src/dotty/tools/dotc/config/Settings.scala +++ /dev/null @@ -1,270 +0,0 @@ -package dotty.tools.dotc -package config - -import collection.mutable.{ ArrayBuffer } -import scala.util.{ Try, Success, Failure } -import scala.reflect.internal.util.StringOps -import reflect.ClassTag -import core.Contexts._ -// import annotation.unchecked - // Dotty deviation: Imports take precedence over definitions in enclosing package - // (Note that @unchecked is in scala, not annotation, so annotation.unchecked gives - // us a package, which is not what was intended anyway). -import language.existentials - -object Settings { - - val BooleanTag = ClassTag.Boolean - val IntTag = ClassTag.Int - val StringTag = ClassTag(classOf[String]) - val ListTag = ClassTag(classOf[List[_]]) - val VersionTag = ClassTag(classOf[ScalaVersion]) - val OptionTag = ClassTag(classOf[Option[_]]) - - class SettingsState(initialValues: Seq[Any]) { - private var values = ArrayBuffer(initialValues: _*) - private var _wasRead: Boolean = false - - override def toString = s"SettingsState(values: ${values.toList})" - - def value(idx: Int): Any = { - _wasRead = true - values(idx) - } - - def update(idx: Int, x: Any): SettingsState = - if (_wasRead) - new SettingsState(values).update(idx, x) - else { - values(idx) = x - this - } - } - - case class ArgsSummary( - sstate: SettingsState, - arguments: List[String], - errors: List[String]) { - - def fail(msg: String) = - ArgsSummary(sstate, arguments, errors :+ msg) - } - - case class Setting[T: ClassTag] private[Settings] ( - name: String, - description: String, - default: T, - helpArg: String = "", - choices: Seq[T] = Nil, - prefix: String = "", - aliases: List[String] = Nil, - depends: List[(Setting[_], Any)] = Nil, - propertyClass: Option[Class[_]] = None)(private[Settings] val idx: Int) { - - def withAbbreviation(abbrv: String): Setting[T] = - copy(aliases = aliases :+ abbrv)(idx) - - def dependsOn[U](setting: Setting[U], value: U): Setting[T] = - copy(depends = depends :+ (setting, value))(idx) - - def valueIn(state: SettingsState): T = - state.value(idx).asInstanceOf[T] - - def updateIn(state: SettingsState, x: Any): SettingsState = x match { - case _: T => state.update(idx, x) - case _ => - // would like to do: - // throw new ClassCastException(s"illegal argument, found: $x of type ${x.getClass}, required: ${implicitly[ClassTag[T]]}") - // but this runs afoul of primitive types. Concretely: if T is Boolean, then x is a boxed Boolean and the test will fail. - // Maybe this is a bug in Scala 2.10? - state.update(idx, x.asInstanceOf[T]) - } - - def isDefaultIn(state: SettingsState) = valueIn(state) == default - - def legalChoices: String = - if (choices.isEmpty) "" - else choices match { - case r: Range => r.head + ".." + r.last - case xs: List[_] => xs.mkString(", ") - } - - def isLegal(arg: Any): Boolean = - if (choices.isEmpty) - arg match { - case _: T => true - case _ => false - } - else choices match { - case r: Range => - arg match { - case x: Int => r.head <= x && x <= r.last - case _ => false - } - case xs: List[_] => - xs contains arg - } - - def tryToSet(state: ArgsSummary): ArgsSummary = { - val ArgsSummary(sstate, arg :: args, errors) = state - def update(value: Any, args: List[String]) = - ArgsSummary(updateIn(sstate, value), args, errors) - def fail(msg: String, args: List[String]) = - ArgsSummary(sstate, args, errors :+ msg) - def missingArg = - fail(s"missing argument for option $name", args) - def doSet(argRest: String) = ((implicitly[ClassTag[T]], args): @unchecked) match { - case (BooleanTag, _) => - update(true, args) - case (OptionTag, _) => - update(Some(propertyClass.get.newInstance), args) - case (ListTag, _) => - if (argRest.isEmpty) missingArg - else update((argRest split ",").toList, args) - case (StringTag, _) if choices.nonEmpty => - if (argRest.isEmpty) missingArg - else if (!choices.contains(argRest)) - fail(s"$arg is not a valid choice for $name", args) - else update(argRest, args) - case (StringTag, arg2 :: args2) => - update(arg2, args2) - case (IntTag, arg2 :: args2) => - try { - val x = arg2.toInt - choices match { - case r: Range if x < r.head || r.last < x => - fail(s"$arg2 is out of legal range $legalChoices for $name", args2) - case _ => - update(x, args2) - } - } catch { - case _: NumberFormatException => - fail(s"$arg2 is not an integer argument for $name", args2) - } - case (VersionTag, _) => - ScalaVersion.parse(argRest) match { - case Success(v) => update(v, args) - case Failure(ex) => fail(ex.getMessage, args) - } - case (_, Nil) => - missingArg - } - - if (prefix != "" && arg.startsWith(prefix)) - doSet(arg drop prefix.length) - else if (prefix == "" && name == arg.takeWhile(_ != ':')) - doSet(arg.dropWhile(_ != ':').drop(1)) - else - state - } - } - - object Setting { - implicit class SettingDecorator[T](val setting: Setting[T]) extends AnyVal { - def value(implicit ctx: Context): T = setting.valueIn(ctx.sstate) - def update(x: T)(implicit ctx: Context): SettingsState = setting.updateIn(ctx.sstate, x) - def isDefault(implicit ctx: Context): Boolean = setting.isDefaultIn(ctx.sstate) - } - } - - class SettingGroup { - - val _allSettings = new ArrayBuffer[Setting[_]] - def allSettings: Seq[Setting[_]] = _allSettings - - def defaultState = new SettingsState(allSettings map (_.default)) - - def userSetSettings(state: SettingsState) = - allSettings filterNot (_.isDefaultIn(state)) - - def toConciseString(state: SettingsState) = - userSetSettings(state).mkString("(", " ", ")") - - private def checkDependencies(state: ArgsSummary): ArgsSummary = - (state /: userSetSettings(state.sstate))(checkDependenciesOfSetting) - - private def checkDependenciesOfSetting(state: ArgsSummary, setting: Setting[_]) = - (state /: setting.depends) { (s, dep) => - val (depSetting, reqValue) = dep - if (depSetting.valueIn(state.sstate) == reqValue) s - else s.fail(s"incomplete option ${setting.name} (requires ${depSetting.name})") - } - - /** Iterates over the arguments applying them to settings where applicable. - * Then verifies setting dependencies are met. - * - * This temporarily takes a boolean indicating whether to keep - * processing if an argument is seen which is not a command line option. - * This is an expedience for the moment so that you can say - * - * scalac -d /tmp foo.scala -optimise - * - * while also allowing - * - * scala Program opt opt - * - * to get their arguments. - */ - protected def processArguments(state: ArgsSummary, processAll: Boolean, skipped: List[String]): ArgsSummary = { - def stateWithArgs(args: List[String]) = ArgsSummary(state.sstate, args, state.errors) - state.arguments match { - case Nil => - checkDependencies(stateWithArgs(skipped)) - case "--" :: args => - checkDependencies(stateWithArgs(skipped ++ args)) - case x :: _ if x startsWith "-" => - def loop(settings: List[Setting[_]]): ArgsSummary = settings match { - case setting :: settings1 => - val state1 = setting.tryToSet(state) - if (state1 ne state) processArguments(state1, processAll, skipped) - else loop(settings1) - case Nil => - state.fail(s"bad option: '$x'") - } - loop(allSettings.toList) - case arg :: args => - if (processAll) processArguments(stateWithArgs(args), processAll, skipped :+ arg) - else state - } - } - - def processArguments(arguments: List[String], processAll: Boolean)(implicit ctx: Context): ArgsSummary = - processArguments(ArgsSummary(ctx.sstate, arguments, Nil), processAll, Nil) - - def publish[T](settingf: Int => Setting[T]): Setting[T] = { - val setting = settingf(_allSettings.length) - _allSettings += setting - setting - } - - def BooleanSetting(name: String, descr: String, initialValue: Boolean = false): Setting[Boolean] = - publish(Setting(name, descr, initialValue)) - - def StringSetting(name: String, helpArg: String, descr: String, default: String): Setting[String] = - publish(Setting(name, descr, default, helpArg)) - - def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): Setting[String] = - publish(Setting(name, descr, default, helpArg, choices)) - - def IntSetting(name: String, descr: String, default: Int, range: Seq[Int] = Nil): Setting[Int] = - publish(Setting(name, descr, default, choices = range)) - - def MultiStringSetting(name: String, helpArg: String, descr: String): Setting[List[String]] = - publish(Setting(name, descr, Nil, helpArg)) - - def PathSetting(name: String, descr: String, default: String): Setting[String] = - publish(Setting(name, descr, default)) - - def PhasesSetting(name: String, descr: String, default: String = ""): Setting[List[String]] = - publish(Setting(name, descr, if (default.isEmpty) Nil else List(default))) - - def PrefixSetting(name: String, pre: String, descr: String): Setting[List[String]] = - publish(Setting(name, descr, Nil, prefix = pre)) - - def VersionSetting(name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] = - publish(Setting(name, descr, default)) - - def OptionSetting[T: ClassTag](name: String, descr: String): Setting[Option[T]] = - publish(Setting(name, descr, None, propertyClass = Some(implicitly[ClassTag[T]].runtimeClass))) - } -} diff --git a/src/dotty/tools/dotc/config/WrappedProperties.scala b/src/dotty/tools/dotc/config/WrappedProperties.scala deleted file mode 100644 index 07972b99b..000000000 --- a/src/dotty/tools/dotc/config/WrappedProperties.scala +++ /dev/null @@ -1,34 +0,0 @@ -package dotty.tools -package dotc -package config - -import java.security.AccessControlException - -/** For placing a wrapper function around property functions. - * Motivated by places like google app engine throwing exceptions - * on property lookups. - */ -trait WrappedProperties extends PropertiesTrait { - def wrap[T](body: => T): Option[T] - - protected def propCategory = "wrapped" - protected def pickJarBasedOn = this.getClass - - override def propIsSet(name: String) = wrap(super.propIsSet(name)) exists (x => x) - override def propOrElse(name: String, alt: String) = wrap(super.propOrElse(name, alt)) getOrElse alt - override def setProp(name: String, value: String) = wrap(super.setProp(name, value)).orNull - override def clearProp(name: String) = wrap(super.clearProp(name)).orNull - override def envOrElse(name: String, alt: String) = wrap(super.envOrElse(name, alt)) getOrElse alt - override def envOrNone(name: String) = wrap(super.envOrNone(name)).flatten - - def systemProperties: Iterator[(String, String)] = { - import scala.collection.JavaConverters._ - wrap(System.getProperties.asScala.iterator) getOrElse Iterator.empty - } -} - -object WrappedProperties { - object AccessControl extends WrappedProperties { - def wrap[T](body: => T) = try Some(body) catch { case _: AccessControlException => None } - } -} diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala deleted file mode 100644 index 0e8e5a1f0..000000000 --- a/src/dotty/tools/dotc/core/Annotations.scala +++ /dev/null @@ -1,162 +0,0 @@ -package dotty.tools.dotc -package core - -import Symbols._, Types._, util.Positions._, Contexts._, Constants._, ast.tpd._ -import config.ScalaVersion -import StdNames._ -import dotty.tools.dotc.ast.{tpd, untpd} - -object Annotations { - - abstract class Annotation { - def tree(implicit ctx: Context): Tree - def symbol(implicit ctx: Context): Symbol = - if (tree.symbol.isConstructor) tree.symbol.owner - else tree.tpe.typeSymbol - def matches(cls: Symbol)(implicit ctx: Context): Boolean = symbol.derivesFrom(cls) - def appliesToModule: Boolean = true // for now; see remark in SymDenotations - - def derivedAnnotation(tree: Tree)(implicit ctx: Context) = - if (tree eq this.tree) this else Annotation(tree) - - def arguments(implicit ctx: Context) = ast.tpd.arguments(tree) - def argument(i: Int)(implicit ctx: Context): Option[Tree] = { - val args = arguments - if (i < args.length) Some(args(i)) else None - } - def argumentConstant(i: Int)(implicit ctx: Context): Option[Constant] = - for (ConstantType(c) <- argument(i) map (_.tpe)) yield c - - def ensureCompleted(implicit ctx: Context): Unit = tree - } - - case class ConcreteAnnotation(t: Tree) extends Annotation { - def tree(implicit ctx: Context): Tree = t - } - - abstract case class LazyAnnotation(sym: Symbol) extends Annotation { - private var myTree: Tree = null - def tree(implicit ctx: Context) = { - if (myTree == null) myTree = complete(ctx) - myTree - } - def complete(implicit ctx: Context): Tree - override def symbol(implicit ctx: Context): Symbol = sym - } - - /** An annotation indicating the body of a right-hand side, - * typically of an inline method. Treated specially in - * pickling/unpickling and TypeTreeMaps - */ - abstract class BodyAnnotation extends Annotation { - override def symbol(implicit ctx: Context) = defn.BodyAnnot - override def derivedAnnotation(tree: Tree)(implicit ctx: Context) = - if (tree eq this.tree) this else ConcreteBodyAnnotation(tree) - override def arguments(implicit ctx: Context) = Nil - override def ensureCompleted(implicit ctx: Context) = () - } - - case class ConcreteBodyAnnotation(body: Tree) extends BodyAnnotation { - def tree(implicit ctx: Context) = body - } - - case class LazyBodyAnnotation(bodyExpr: Context => Tree) extends BodyAnnotation { - private var evaluated = false - private var myBody: Tree = _ - def tree(implicit ctx: Context) = { - if (evaluated) assert(myBody != null) - else { - evaluated = true - myBody = bodyExpr(ctx) - } - myBody - } - def isEvaluated = evaluated - } - - object Annotation { - - def apply(tree: Tree) = ConcreteAnnotation(tree) - - def apply(cls: ClassSymbol)(implicit ctx: Context): Annotation = - apply(cls, Nil) - - def apply(cls: ClassSymbol, arg: Tree)(implicit ctx: Context): Annotation = - apply(cls, arg :: Nil) - - def apply(cls: ClassSymbol, arg1: Tree, arg2: Tree)(implicit ctx: Context): Annotation = - apply(cls, arg1 :: arg2 :: Nil) - - def apply(cls: ClassSymbol, args: List[Tree])(implicit ctx: Context): Annotation = - apply(cls.typeRef, args) - - def apply(atp: Type, arg: Tree)(implicit ctx: Context): Annotation = - apply(atp, arg :: Nil) - - def apply(atp: Type, arg1: Tree, arg2: Tree)(implicit ctx: Context): Annotation = - apply(atp, arg1 :: arg2 :: Nil) - - def apply(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation = - apply(New(atp, args)) - - private def resolveConstructor(atp: Type, args:List[Tree])(implicit ctx: Context): Tree = { - val targs = atp.argTypes - tpd.applyOverloaded(New(atp withoutArgs targs), nme.CONSTRUCTOR, args, targs, atp, isAnnotConstructor = true) - } - - def applyResolve(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation = { - apply(resolveConstructor(atp, args)) - } - - def deferred(sym: Symbol, treeFn: Context => Tree)(implicit ctx: Context): Annotation = - new LazyAnnotation(sym) { - def complete(implicit ctx: Context) = treeFn(ctx) - } - - def deferred(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation = - deferred(atp.classSymbol, implicit ctx => New(atp, args)) - - def deferredResolve(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation = - deferred(atp.classSymbol, implicit ctx => resolveConstructor(atp, args)) - - def makeAlias(sym: TermSymbol)(implicit ctx: Context) = - apply(defn.AliasAnnot, List( - ref(TermRef.withSigAndDenot(sym.owner.thisType, sym.name, sym.signature, sym)))) - - def makeChild(sym: Symbol)(implicit ctx: Context) = - deferred(defn.ChildAnnot, - implicit ctx => New(defn.ChildAnnotType.appliedTo(sym.owner.thisType.select(sym.name, sym)), Nil)) - - def makeSourceFile(path: String)(implicit ctx: Context) = - apply(defn.SourceFileAnnot, Literal(Constant(path))) - } - - def ThrowsAnnotation(cls: ClassSymbol)(implicit ctx: Context) = { - val tref = cls.typeRef - Annotation(defn.ThrowsAnnotType.appliedTo(tref), Ident(tref)) - } - - /** A decorator that provides queries for specific annotations - * of a symbol. - */ - implicit class AnnotInfo(val sym: Symbol) extends AnyVal { - - def isDeprecated(implicit ctx: Context) = - sym.hasAnnotation(defn.DeprecatedAnnot) - - def deprecationMessage(implicit ctx: Context) = - for (annot <- sym.getAnnotation(defn.DeprecatedAnnot); - arg <- annot.argumentConstant(0)) - yield arg.stringValue - - def migrationVersion(implicit ctx: Context) = - for (annot <- sym.getAnnotation(defn.MigrationAnnot); - arg <- annot.argumentConstant(1)) - yield ScalaVersion.parse(arg.stringValue) - - def migrationMessage(implicit ctx: Context) = - for (annot <- sym.getAnnotation(defn.MigrationAnnot); - arg <- annot.argumentConstant(0)) - yield ScalaVersion.parse(arg.stringValue) - } -} diff --git a/src/dotty/tools/dotc/core/CheckRealizable.scala b/src/dotty/tools/dotc/core/CheckRealizable.scala deleted file mode 100644 index 78ec685fc..000000000 --- a/src/dotty/tools/dotc/core/CheckRealizable.scala +++ /dev/null @@ -1,132 +0,0 @@ -package dotty.tools -package dotc -package core - -import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._ -import SymDenotations._, Denotations.SingleDenotation -import util.Positions._ -import Decorators._ -import StdNames._ -import Annotations._ -import collection.mutable -import ast.tpd._ - -/** Realizability status */ -object CheckRealizable { - - abstract class Realizability(val msg: String) { - def andAlso(other: => Realizability) = - if (this == Realizable) other else this - def mapError(f: Realizability => Realizability) = - if (this == Realizable) this else f(this) - } - - object Realizable extends Realizability("") - - object NotConcrete extends Realizability(" is not a concrete type") - - object NotStable extends Realizability(" is not a stable reference") - - class NotFinal(sym: Symbol)(implicit ctx: Context) - extends Realizability(i" refers to nonfinal $sym") - - class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context) - extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}") - - class HasProblemField(fld: SingleDenotation, problem: Realizability)(implicit ctx: Context) - extends Realizability(i" has a member $fld which is not a legal path\n since ${fld.symbol.name}: ${fld.info}${problem.msg}") - - class ProblemInUnderlying(tp: Type, problem: Realizability)(implicit ctx: Context) - extends Realizability(i"s underlying type ${tp}${problem.msg}") { - assert(problem != Realizable) - } - - def realizability(tp: Type)(implicit ctx: Context) = - new CheckRealizable().realizability(tp) - - def boundsRealizability(tp: Type)(implicit ctx: Context) = - new CheckRealizable().boundsRealizability(tp) -} - -/** Compute realizability status */ -class CheckRealizable(implicit ctx: Context) { - import CheckRealizable._ - - /** A set of all fields that have already been checked. Used - * to avoid infinite recursions when analyzing recursive types. - */ - private val checkedFields: mutable.Set[Symbol] = mutable.LinkedHashSet[Symbol]() - - /** Is symbol's definitition a lazy val? - * (note we exclude modules here, because their realizability is ensured separately) - */ - private def isLateInitialized(sym: Symbol) = sym.is(Lazy, butNot = Module) - - /** The realizability status of given type `tp`*/ - def realizability(tp: Type): Realizability = tp.dealias match { - case tp: TermRef => - val sym = tp.symbol - if (sym.is(Stable)) realizability(tp.prefix) - else { - val r = - if (!sym.isStable) NotStable - else if (!isLateInitialized(sym)) realizability(tp.prefix) - else if (!sym.isEffectivelyFinal) new NotFinal(sym) - else realizability(tp.info).mapError(r => new ProblemInUnderlying(tp.info, r)) - if (r == Realizable) sym.setFlag(Stable) - r - } - case _: SingletonType | NoPrefix => - Realizable - case tp => - def isConcrete(tp: Type): Boolean = tp.dealias match { - case tp: TypeRef => tp.symbol.isClass - case tp: TypeProxy => isConcrete(tp.underlying) - case tp: AndOrType => isConcrete(tp.tp1) && isConcrete(tp.tp2) - case _ => false - } - if (!isConcrete(tp)) NotConcrete - else boundsRealizability(tp).andAlso(memberRealizability(tp)) - } - - /** `Realizable` if `tp` has good bounds, a `HasProblemBounds` instance - * pointing to a bad bounds member otherwise. - */ - private def boundsRealizability(tp: Type) = { - def hasBadBounds(mbr: SingleDenotation) = { - val bounds = mbr.info.bounds - !(bounds.lo <:< bounds.hi) - } - tp.nonClassTypeMembers.find(hasBadBounds) match { - case Some(mbr) => new HasProblemBounds(mbr) - case _ => Realizable - } - } - - /** `Realizable` if all of `tp`'s non-struct fields have realizable types, - * a `HasProblemField` instance pointing to a bad field otherwise. - */ - private def memberRealizability(tp: Type) = { - def checkField(sofar: Realizability, fld: SingleDenotation): Realizability = - sofar andAlso { - if (checkedFields.contains(fld.symbol) || fld.symbol.is(Private | Mutable | Lazy)) - // if field is private it cannot be part of a visible path - // if field is mutable it cannot be part of a path - // if field is lazy it does not need to be initialized when the owning object is - // so in all cases the field does not influence realizability of the enclosing object. - Realizable - else { - checkedFields += fld.symbol - realizability(fld.info).mapError(r => new HasProblemField(fld, r)) - } - } - if (ctx.settings.strict.value) - // check fields only under strict mode for now. - // Reason: An embedded field could well be nullable, which means it - // should not be part of a path and need not be checked; but we cannot recognize - // this situation until we have a typesystem that tracks nullability. - ((Realizable: Realizability) /: tp.fields)(checkField) - else - Realizable - } -} diff --git a/src/dotty/tools/dotc/core/Comments.scala b/src/dotty/tools/dotc/core/Comments.scala deleted file mode 100644 index 1e623db4d..000000000 --- a/src/dotty/tools/dotc/core/Comments.scala +++ /dev/null @@ -1,459 +0,0 @@ -package dotty.tools -package dotc -package core - -import ast.{ untpd, tpd } -import Decorators._, Symbols._, Contexts._, Flags.EmptyFlags -import util.SourceFile -import util.Positions._ -import util.CommentParsing._ -import util.Property.Key -import parsing.Parsers.Parser -import reporting.diagnostic.messages.ProperDefinitionNotFound - -object Comments { - val ContextDoc = new Key[ContextDocstrings] - - /** Decorator for getting docbase out of context */ - implicit class CommentsContext(val ctx: Context) extends AnyVal { - def docCtx: Option[ContextDocstrings] = ctx.property(ContextDoc) - } - - /** Context for Docstrings, contains basic functionality for getting - * docstrings via `Symbol` and expanding templates - */ - class ContextDocstrings { - import scala.collection.mutable - - private[this] val _docstrings: mutable.Map[Symbol, Comment] = - mutable.Map.empty - - val templateExpander = new CommentExpander - - def docstrings: Map[Symbol, Comment] = _docstrings.toMap - - def docstring(sym: Symbol): Option[Comment] = _docstrings.get(sym) - - def addDocstring(sym: Symbol, doc: Option[Comment]): Unit = - doc.map(d => _docstrings += (sym -> d)) - } - - /** A `Comment` contains the unformatted docstring as well as a position - * - * The `Comment` contains functionality to create versions of itself without - * `@usecase` sections as well as functionality to map the `raw` docstring - */ - abstract case class Comment(pos: Position, raw: String) { self => - def isExpanded: Boolean - - def usecases: List[UseCase] - - val isDocComment = raw.startsWith("/**") - - def expand(f: String => String): Comment = new Comment(pos, f(raw)) { - val isExpanded = true - val usecases = self.usecases - } - - def withUsecases(implicit ctx: Context): Comment = new Comment(pos, stripUsecases) { - val isExpanded = self.isExpanded - val usecases = parseUsecases - } - - private[this] lazy val stripUsecases: String = - removeSections(raw, "@usecase", "@define") - - private[this] def parseUsecases(implicit ctx: Context): List[UseCase] = - if (!raw.startsWith("/**")) - List.empty[UseCase] - else - tagIndex(raw) - .filter { startsWithTag(raw, _, "@usecase") } - .map { case (start, end) => decomposeUseCase(start, end) } - - /** Turns a usecase section into a UseCase, with code changed to: - * {{{ - * // From: - * def foo: A - * // To: - * def foo: A = ??? - * }}} - */ - private[this] def decomposeUseCase(start: Int, end: Int)(implicit ctx: Context): UseCase = { - def subPos(start: Int, end: Int) = - if (pos == NoPosition) NoPosition - else { - val start1 = pos.start + start - val end1 = pos.end + end - pos withStart start1 withPoint start1 withEnd end1 - } - - val codeStart = skipWhitespace(raw, start + "@usecase".length) - val codeEnd = skipToEol(raw, codeStart) - val code = raw.substring(codeStart, codeEnd) + " = ???" - val codePos = subPos(codeStart, codeEnd) - val commentStart = skipLineLead(raw, codeEnd + 1) min end - val commentStr = "/** " + raw.substring(commentStart, end) + "*/" - val commentPos = subPos(commentStart, end) - - UseCase(Comment(commentPos, commentStr), code, codePos) - } - } - - object Comment { - def apply(pos: Position, raw: String, expanded: Boolean = false, usc: List[UseCase] = Nil)(implicit ctx: Context): Comment = - new Comment(pos, raw) { - val isExpanded = expanded - val usecases = usc - } - } - - abstract case class UseCase(comment: Comment, code: String, codePos: Position) { - /** Set by typer */ - var tpdCode: tpd.DefDef = _ - - def untpdCode: untpd.Tree - } - - object UseCase { - def apply(comment: Comment, code: String, codePos: Position)(implicit ctx: Context) = - new UseCase(comment, code, codePos) { - val untpdCode = { - val tree = new Parser(new SourceFile("<usecase>", code)).localDef(codePos.start, EmptyFlags) - - tree match { - case tree: untpd.DefDef => - val newName = (tree.name.show + "$" + codePos + "$doc").toTermName - untpd.DefDef(newName, tree.tparams, tree.vparamss, tree.tpt, tree.rhs) - case _ => - ctx.error(ProperDefinitionNotFound(), codePos) - tree - } - } - } - } - - /** - * Port of DocComment.scala from nsc - * @author Martin Odersky - * @author Felix Mulder - */ - class CommentExpander { - import dotc.config.Printers.dottydoc - import scala.collection.mutable - - def expand(sym: Symbol, site: Symbol)(implicit ctx: Context): String = { - val parent = if (site != NoSymbol) site else sym - defineVariables(parent) - expandedDocComment(sym, parent) - } - - /** The cooked doc comment of symbol `sym` after variable expansion, or "" if missing. - * - * @param sym The symbol for which doc comment is returned - * @param site The class for which doc comments are generated - * @throws ExpansionLimitExceeded when more than 10 successive expansions - * of the same string are done, which is - * interpreted as a recursive variable definition. - */ - def expandedDocComment(sym: Symbol, site: Symbol, docStr: String = "")(implicit ctx: Context): String = { - // when parsing a top level class or module, use the (module-)class itself to look up variable definitions - val parent = if ((sym.is(Flags.Module) || sym.isClass) && site.is(Flags.Package)) sym - else site - expandVariables(cookedDocComment(sym, docStr), sym, parent) - } - - private def template(raw: String): String = - removeSections(raw, "@define") - - private def defines(raw: String): List[String] = { - val sections = tagIndex(raw) - val defines = sections filter { startsWithTag(raw, _, "@define") } - val usecases = sections filter { startsWithTag(raw, _, "@usecase") } - val end = startTag(raw, (defines ::: usecases).sortBy(_._1)) - - defines map { case (start, end) => raw.substring(start, end) } - } - - private def replaceInheritDocToInheritdoc(docStr: String): String = - docStr.replaceAll("""\{@inheritDoc\p{Zs}*\}""", "@inheritdoc") - - /** The cooked doc comment of an overridden symbol */ - protected def superComment(sym: Symbol)(implicit ctx: Context): Option[String] = - allInheritedOverriddenSymbols(sym).iterator map (x => cookedDocComment(x)) find (_ != "") - - private val cookedDocComments = mutable.HashMap[Symbol, String]() - - /** The raw doc comment of symbol `sym`, minus usecase and define sections, augmented by - * missing sections of an inherited doc comment. - * If a symbol does not have a doc comment but some overridden version of it does, - * the doc comment of the overridden version is copied instead. - */ - def cookedDocComment(sym: Symbol, docStr: String = "")(implicit ctx: Context): String = cookedDocComments.getOrElseUpdate(sym, { - var ownComment = - if (docStr.length == 0) ctx.docCtx.flatMap(_.docstring(sym).map(c => template(c.raw))).getOrElse("") - else template(docStr) - ownComment = replaceInheritDocToInheritdoc(ownComment) - - superComment(sym) match { - case None => - // SI-8210 - The warning would be false negative when this symbol is a setter - if (ownComment.indexOf("@inheritdoc") != -1 && ! sym.isSetter) - dottydoc.println(s"${sym.pos}: the comment for ${sym} contains @inheritdoc, but no parent comment is available to inherit from.") - ownComment.replaceAllLiterally("@inheritdoc", "<invalid inheritdoc annotation>") - case Some(sc) => - if (ownComment == "") sc - else expandInheritdoc(sc, merge(sc, ownComment, sym), sym) - } - }) - - private def isMovable(str: String, sec: (Int, Int)): Boolean = - startsWithTag(str, sec, "@param") || - startsWithTag(str, sec, "@tparam") || - startsWithTag(str, sec, "@return") - - def merge(src: String, dst: String, sym: Symbol, copyFirstPara: Boolean = false): String = { - val srcSections = tagIndex(src) - val dstSections = tagIndex(dst) - val srcParams = paramDocs(src, "@param", srcSections) - val dstParams = paramDocs(dst, "@param", dstSections) - val srcTParams = paramDocs(src, "@tparam", srcSections) - val dstTParams = paramDocs(dst, "@tparam", dstSections) - val out = new StringBuilder - var copied = 0 - var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _))) - - if (copyFirstPara) { - val eop = // end of comment body (first para), which is delimited by blank line, or tag, or end of comment - (findNext(src, 0)(src.charAt(_) == '\n')) min startTag(src, srcSections) - out append src.substring(0, eop).trim - copied = 3 - tocopy = 3 - } - - def mergeSection(srcSec: Option[(Int, Int)], dstSec: Option[(Int, Int)]) = dstSec match { - case Some((start, end)) => - if (end > tocopy) tocopy = end - case None => - srcSec match { - case Some((start1, end1)) => { - out append dst.substring(copied, tocopy).trim - out append "\n" - copied = tocopy - out append src.substring(start1, end1).trim - } - case None => - } - } - - //TODO: enable this once you know how to get `sym.paramss` - /* - for (params <- sym.paramss; param <- params) - mergeSection(srcParams get param.name.toString, dstParams get param.name.toString) - for (tparam <- sym.typeParams) - mergeSection(srcTParams get tparam.name.toString, dstTParams get tparam.name.toString) - - mergeSection(returnDoc(src, srcSections), returnDoc(dst, dstSections)) - mergeSection(groupDoc(src, srcSections), groupDoc(dst, dstSections)) - */ - - if (out.length == 0) dst - else { - out append dst.substring(copied) - out.toString - } - } - - /** - * Expand inheritdoc tags - * - for the main comment we transform the inheritdoc into the super variable, - * and the variable expansion can expand it further - * - for the param, tparam and throws sections we must replace comments on the spot - * - * This is done separately, for two reasons: - * 1. It takes longer to run compared to merge - * 2. The inheritdoc annotation should not be used very often, as building the comment from pieces severely - * impacts performance - * - * @param parent The source (or parent) comment - * @param child The child (overriding member or usecase) comment - * @param sym The child symbol - * @return The child comment with the inheritdoc sections expanded - */ - def expandInheritdoc(parent: String, child: String, sym: Symbol): String = - if (child.indexOf("@inheritdoc") == -1) - child - else { - val parentSections = tagIndex(parent) - val childSections = tagIndex(child) - val parentTagMap = sectionTagMap(parent, parentSections) - val parentNamedParams = Map() + - ("@param" -> paramDocs(parent, "@param", parentSections)) + - ("@tparam" -> paramDocs(parent, "@tparam", parentSections)) + - ("@throws" -> paramDocs(parent, "@throws", parentSections)) - - val out = new StringBuilder - - def replaceInheritdoc(childSection: String, parentSection: => String) = - if (childSection.indexOf("@inheritdoc") == -1) - childSection - else - childSection.replaceAllLiterally("@inheritdoc", parentSection) - - def getParentSection(section: (Int, Int)): String = { - - def getSectionHeader = extractSectionTag(child, section) match { - case param@("@param"|"@tparam"|"@throws") => param + " " + extractSectionParam(child, section) - case other => other - } - - def sectionString(param: String, paramMap: Map[String, (Int, Int)]): String = - paramMap.get(param) match { - case Some(section) => - // Cleanup the section tag and parameter - val sectionTextBounds = extractSectionText(parent, section) - cleanupSectionText(parent.substring(sectionTextBounds._1, sectionTextBounds._2)) - case None => - dottydoc.println(s"""${sym.pos}: the """" + getSectionHeader + "\" annotation of the " + sym + - " comment contains @inheritdoc, but the corresponding section in the parent is not defined.") - "<invalid inheritdoc annotation>" - } - - child.substring(section._1, section._1 + 7) match { - case param@("@param "|"@tparam"|"@throws") => - sectionString(extractSectionParam(child, section), parentNamedParams(param.trim)) - case _ => - sectionString(extractSectionTag(child, section), parentTagMap) - } - } - - def mainComment(str: String, sections: List[(Int, Int)]): String = - if (str.trim.length > 3) - str.trim.substring(3, startTag(str, sections)) - else - "" - - // Append main comment - out.append("/**") - out.append(replaceInheritdoc(mainComment(child, childSections), mainComment(parent, parentSections))) - - // Append sections - for (section <- childSections) - out.append(replaceInheritdoc(child.substring(section._1, section._2), getParentSection(section))) - - out.append("*/") - out.toString - } - - protected def expandVariables(initialStr: String, sym: Symbol, site: Symbol)(implicit ctx: Context): String = { - val expandLimit = 10 - - def expandInternal(str: String, depth: Int): String = { - if (depth >= expandLimit) - throw new ExpansionLimitExceeded(str) - - val out = new StringBuilder - var copied, idx = 0 - // excluding variables written as \$foo so we can use them when - // necessary to document things like Symbol#decode - def isEscaped = idx > 0 && str.charAt(idx - 1) == '\\' - while (idx < str.length) { - if ((str charAt idx) != '$' || isEscaped) - idx += 1 - else { - val vstart = idx - idx = skipVariable(str, idx + 1) - def replaceWith(repl: String) = { - out append str.substring(copied, vstart) - out append repl - copied = idx - } - variableName(str.substring(vstart + 1, idx)) match { - case "super" => - superComment(sym) foreach { sc => - val superSections = tagIndex(sc) - replaceWith(sc.substring(3, startTag(sc, superSections))) - for (sec @ (start, end) <- superSections) - if (!isMovable(sc, sec)) out append sc.substring(start, end) - } - case "" => idx += 1 - case vname => - lookupVariable(vname, site) match { - case Some(replacement) => replaceWith(replacement) - case None => - dottydoc.println(s"Variable $vname undefined in comment for $sym in $site") - } - } - } - } - if (out.length == 0) str - else { - out append str.substring(copied) - expandInternal(out.toString, depth + 1) - } - } - - // We suppressed expanding \$ throughout the recursion, and now we - // need to replace \$ with $ so it looks as intended. - expandInternal(initialStr, 0).replaceAllLiterally("""\$""", "$") - } - - def defineVariables(sym: Symbol)(implicit ctx: Context) = { - val Trim = "(?s)^[\\s&&[^\n\r]]*(.*?)\\s*$".r - - val raw = ctx.docCtx.flatMap(_.docstring(sym).map(_.raw)).getOrElse("") - defs(sym) ++= defines(raw).map { - str => { - val start = skipWhitespace(str, "@define".length) - val (key, value) = str.splitAt(skipVariable(str, start)) - key.drop(start) -> value - } - } map { - case (key, Trim(value)) => - variableName(key) -> value.replaceAll("\\s+\\*+$", "") - } - } - - /** Maps symbols to the variable -> replacement maps that are defined - * in their doc comments - */ - private val defs = mutable.HashMap[Symbol, Map[String, String]]() withDefaultValue Map() - - /** Lookup definition of variable. - * - * @param vble The variable for which a definition is searched - * @param site The class for which doc comments are generated - */ - def lookupVariable(vble: String, site: Symbol)(implicit ctx: Context): Option[String] = site match { - case NoSymbol => None - case _ => - val searchList = - if (site.flags.is(Flags.Module)) site :: site.info.baseClasses - else site.info.baseClasses - - searchList collectFirst { case x if defs(x) contains vble => defs(x)(vble) } match { - case Some(str) if str startsWith "$" => lookupVariable(str.tail, site) - case res => res orElse lookupVariable(vble, site.owner) - } - } - - /** The position of the raw doc comment of symbol `sym`, or NoPosition if missing - * If a symbol does not have a doc comment but some overridden version of it does, - * the position of the doc comment of the overridden version is returned instead. - */ - def docCommentPos(sym: Symbol)(implicit ctx: Context): Position = - ctx.docCtx.flatMap(_.docstring(sym).map(_.pos)).getOrElse(NoPosition) - - /** A version which doesn't consider self types, as a temporary measure: - * an infinite loop has broken out between superComment and cookedDocComment - * since r23926. - */ - private def allInheritedOverriddenSymbols(sym: Symbol)(implicit ctx: Context): List[Symbol] = { - if (!sym.owner.isClass) Nil - else sym.allOverriddenSymbols.toList.filter(_ != NoSymbol) //TODO: could also be `sym.owner.allOverrid..` - //else sym.owner.ancestors map (sym overriddenSymbol _) filter (_ != NoSymbol) - } - - class ExpansionLimitExceeded(str: String) extends Exception - } -} diff --git a/src/dotty/tools/dotc/core/Constants.scala b/src/dotty/tools/dotc/core/Constants.scala deleted file mode 100644 index 1892e4bdc..000000000 --- a/src/dotty/tools/dotc/core/Constants.scala +++ /dev/null @@ -1,235 +0,0 @@ -package dotty.tools.dotc -package core - -import Types._, Symbols._, Contexts._ -import printing.Printer - -object Constants { - - final val NoTag = 0 - final val UnitTag = 1 - final val BooleanTag = 2 - final val ByteTag = 3 - final val ShortTag = 4 - final val CharTag = 5 - final val IntTag = 6 - final val LongTag = 7 - final val FloatTag = 8 - final val DoubleTag = 9 - final val StringTag = 10 - final val NullTag = 11 - final val ClazzTag = 12 - // For supporting java enumerations inside java annotations (see ClassfileParser) - final val EnumTag = 13 - - case class Constant(value: Any) extends printing.Showable { - import java.lang.Double.doubleToRawLongBits - import java.lang.Float.floatToRawIntBits - - val tag: Int = value match { - case null => NullTag - case x: Unit => UnitTag - case x: Boolean => BooleanTag - case x: Byte => ByteTag - case x: Short => ShortTag - case x: Int => IntTag - case x: Long => LongTag - case x: Float => FloatTag - case x: Double => DoubleTag - case x: String => StringTag - case x: Char => CharTag - case x: Type => ClazzTag - case x: Symbol => EnumTag - case _ => throw new Error("bad constant value: " + value + " of class " + value.getClass) - } - - def isByteRange: Boolean = isIntRange && Byte.MinValue <= intValue && intValue <= Byte.MaxValue - def isShortRange: Boolean = isIntRange && Short.MinValue <= intValue && intValue <= Short.MaxValue - def isCharRange: Boolean = isIntRange && Char.MinValue <= intValue && intValue <= Char.MaxValue - def isIntRange: Boolean = ByteTag <= tag && tag <= IntTag - def isLongRange: Boolean = ByteTag <= tag && tag <= LongTag - def isFloatRange: Boolean = ByteTag <= tag && tag <= FloatTag - def isNumeric: Boolean = ByteTag <= tag && tag <= DoubleTag - def isNonUnitAnyVal = BooleanTag <= tag && tag <= DoubleTag - def isAnyVal = UnitTag <= tag && tag <= DoubleTag - - def tpe(implicit ctx: Context): Type = tag match { - case UnitTag => defn.UnitType - case BooleanTag => defn.BooleanType - case ByteTag => defn.ByteType - case ShortTag => defn.ShortType - case CharTag => defn.CharType - case IntTag => defn.IntType - case LongTag => defn.LongType - case FloatTag => defn.FloatType - case DoubleTag => defn.DoubleType - case StringTag => defn.StringType - case NullTag => defn.NullType - case ClazzTag => defn.ClassType(typeValue) - case EnumTag => defn.EnumType(symbolValue) - } - - /** We need the equals method to take account of tags as well as values. - */ - override def equals(other: Any): Boolean = other match { - case that: Constant => - this.tag == that.tag && equalHashValue == that.equalHashValue - case _ => false - } - - def isNaN = value match { - case f: Float => f.isNaN - case d: Double => d.isNaN - case _ => false - } - - def booleanValue: Boolean = - if (tag == BooleanTag) value.asInstanceOf[Boolean] - else throw new Error("value " + value + " is not a boolean") - - def byteValue: Byte = tag match { - case ByteTag => value.asInstanceOf[Byte] - case ShortTag => value.asInstanceOf[Short].toByte - case CharTag => value.asInstanceOf[Char].toByte - case IntTag => value.asInstanceOf[Int].toByte - case LongTag => value.asInstanceOf[Long].toByte - case FloatTag => value.asInstanceOf[Float].toByte - case DoubleTag => value.asInstanceOf[Double].toByte - case _ => throw new Error("value " + value + " is not a Byte") - } - - def shortValue: Short = tag match { - case ByteTag => value.asInstanceOf[Byte].toShort - case ShortTag => value.asInstanceOf[Short] - case CharTag => value.asInstanceOf[Char].toShort - case IntTag => value.asInstanceOf[Int].toShort - case LongTag => value.asInstanceOf[Long].toShort - case FloatTag => value.asInstanceOf[Float].toShort - case DoubleTag => value.asInstanceOf[Double].toShort - case _ => throw new Error("value " + value + " is not a Short") - } - - def charValue: Char = tag match { - case ByteTag => value.asInstanceOf[Byte].toChar - case ShortTag => value.asInstanceOf[Short].toChar - case CharTag => value.asInstanceOf[Char] - case IntTag => value.asInstanceOf[Int].toChar - case LongTag => value.asInstanceOf[Long].toChar - case FloatTag => value.asInstanceOf[Float].toChar - case DoubleTag => value.asInstanceOf[Double].toChar - case _ => throw new Error("value " + value + " is not a Char") - } - - def intValue: Int = tag match { - case ByteTag => value.asInstanceOf[Byte].toInt - case ShortTag => value.asInstanceOf[Short].toInt - case CharTag => value.asInstanceOf[Char].toInt - case IntTag => value.asInstanceOf[Int] - case LongTag => value.asInstanceOf[Long].toInt - case FloatTag => value.asInstanceOf[Float].toInt - case DoubleTag => value.asInstanceOf[Double].toInt - case _ => throw new Error("value " + value + " is not an Int") - } - - def longValue: Long = tag match { - case ByteTag => value.asInstanceOf[Byte].toLong - case ShortTag => value.asInstanceOf[Short].toLong - case CharTag => value.asInstanceOf[Char].toLong - case IntTag => value.asInstanceOf[Int].toLong - case LongTag => value.asInstanceOf[Long] - case FloatTag => value.asInstanceOf[Float].toLong - case DoubleTag => value.asInstanceOf[Double].toLong - case _ => throw new Error("value " + value + " is not a Long") - } - - def floatValue: Float = tag match { - case ByteTag => value.asInstanceOf[Byte].toFloat - case ShortTag => value.asInstanceOf[Short].toFloat - case CharTag => value.asInstanceOf[Char].toFloat - case IntTag => value.asInstanceOf[Int].toFloat - case LongTag => value.asInstanceOf[Long].toFloat - case FloatTag => value.asInstanceOf[Float] - case DoubleTag => value.asInstanceOf[Double].toFloat - case _ => throw new Error("value " + value + " is not a Float") - } - - def doubleValue: Double = tag match { - case ByteTag => value.asInstanceOf[Byte].toDouble - case ShortTag => value.asInstanceOf[Short].toDouble - case CharTag => value.asInstanceOf[Char].toDouble - case IntTag => value.asInstanceOf[Int].toDouble - case LongTag => value.asInstanceOf[Long].toDouble - case FloatTag => value.asInstanceOf[Float].toDouble - case DoubleTag => value.asInstanceOf[Double] - case _ => throw new Error("value " + value + " is not a Double") - } - - /** Convert constant value to conform to given type. - */ - def convertTo(pt: Type)(implicit ctx: Context): Constant = { - def classBound(pt: Type): Type = pt.dealias.stripTypeVar match { - case tref: TypeRef if !tref.symbol.isClass => classBound(tref.info.bounds.lo) - case param: PolyParam => - ctx.typerState.constraint.entry(param) match { - case TypeBounds(lo, hi) => - if (hi.classSymbol.isPrimitiveValueClass) hi //constrain further with high bound - else classBound(lo) - case NoType => classBound(param.binder.paramBounds(param.paramNum).lo) - case inst => classBound(inst) - } - case pt => pt - } - val target = classBound(pt).typeSymbol - if (target == tpe.typeSymbol) - this - else if ((target == defn.ByteClass) && isByteRange) - Constant(byteValue) - else if (target == defn.ShortClass && isShortRange) - Constant(shortValue) - else if (target == defn.CharClass && isCharRange) - Constant(charValue) - else if (target == defn.IntClass && isIntRange) - Constant(intValue) - else if (target == defn.LongClass && isLongRange) - Constant(longValue) - else if (target == defn.FloatClass && isFloatRange) - Constant(floatValue) - else if (target == defn.DoubleClass && isNumeric) - Constant(doubleValue) - else - null - } - - def stringValue: String = value.toString - - def toText(printer: Printer) = printer.toText(this) - - def typeValue: Type = value.asInstanceOf[Type] - def symbolValue: Symbol = value.asInstanceOf[Symbol] - - /** - * Consider two `NaN`s to be identical, despite non-equality - * Consider -0d to be distinct from 0d, despite equality - * - * We use the raw versions (i.e. `floatToRawIntBits` rather than `floatToIntBits`) - * to avoid treating different encodings of `NaN` as the same constant. - * You probably can't express different `NaN` varieties as compile time - * constants in regular Scala code, but it is conceivable that you could - * conjure them with a macro. - */ - private def equalHashValue: Any = value match { - case f: Float => floatToRawIntBits(f) - case d: Double => doubleToRawLongBits(d) - case v => v - } - - override def hashCode: Int = { - import scala.util.hashing.MurmurHash3._ - val seed = 17 - var h = seed - h = mix(h, tag.##) // include tag in the hash, otherwise 0, 0d, 0L, 0f collide. - h = mix(h, equalHashValue.##) - finalizeHash(h, length = 2) - } - } -} diff --git a/src/dotty/tools/dotc/core/Constraint.scala b/src/dotty/tools/dotc/core/Constraint.scala deleted file mode 100644 index c99b748b7..000000000 --- a/src/dotty/tools/dotc/core/Constraint.scala +++ /dev/null @@ -1,154 +0,0 @@ -package dotty.tools -package dotc -package core - -import Types._, Contexts._, Symbols._ -import util.SimpleMap -import collection.mutable -import printing.{Printer, Showable} -import printing.Texts._ -import config.Config -import config.Printers.constr - -/** Constraint over undetermined type parameters. Constraints are built - * over values of the following types: - * - * - PolyType A constraint constrains the type parameters of a set of PolyTypes - * - PolyParam The parameters of the constrained polytypes - * - TypeVar Every constrained parameter might be associated with a TypeVar - * that has the PolyParam as origin. - */ -abstract class Constraint extends Showable { - - type This <: Constraint - - /** Does the constraint's domain contain the type parameters of `pt`? */ - def contains(pt: PolyType): Boolean - - /** Does the constraint's domain contain the type parameter `param`? */ - def contains(param: PolyParam): Boolean - - /** Does this constraint contain the type variable `tvar` and is it uninstantiated? */ - def contains(tvar: TypeVar): Boolean - - /** The constraint entry for given type parameter `param`, or NoType if `param` is not part of - * the constraint domain. Note: Low level, implementation dependent. - */ - def entry(param: PolyParam): Type - - /** The type variable corresponding to parameter `param`, or - * NoType, if `param` is not in constrained or is not paired with a type variable. - */ - def typeVarOfParam(param: PolyParam): Type - - /** Is it known that `param1 <:< param2`? */ - def isLess(param1: PolyParam, param2: PolyParam): Boolean - - /** The parameters that are known to be smaller wrt <: than `param` */ - def lower(param: PolyParam): List[PolyParam] - - /** The parameters that are known to be greater wrt <: than `param` */ - def upper(param: PolyParam): List[PolyParam] - - /** lower(param) \ lower(butNot) */ - def exclusiveLower(param: PolyParam, butNot: PolyParam): List[PolyParam] - - /** upper(param) \ upper(butNot) */ - def exclusiveUpper(param: PolyParam, butNot: PolyParam): List[PolyParam] - - /** The constraint bounds for given type parameter `param`. - * Poly params that are known to be smaller or greater than `param` - * are not contained in the return bounds. - * @pre `param` is not part of the constraint domain. - */ - def nonParamBounds(param: PolyParam): TypeBounds - - /** The lower bound of `param` including all known-to-be-smaller parameters */ - def fullLowerBound(param: PolyParam)(implicit ctx: Context): Type - - /** The upper bound of `param` including all known-to-be-greater parameters */ - def fullUpperBound(param: PolyParam)(implicit ctx: Context): Type - - /** The bounds of `param` including all known-to-be-smaller and -greater parameters */ - def fullBounds(param: PolyParam)(implicit ctx: Context): TypeBounds - - /** A new constraint which is derived from this constraint by adding - * entries for all type parameters of `poly`. - * @param tvars A list of type variables associated with the params, - * or Nil if the constraint will just be checked for - * satisfiability but will solved to give instances of - * type variables. - */ - def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This - - /** A new constraint which is derived from this constraint by updating - * the entry for parameter `param` to `tp`. - * `tp` can be one of the following: - * - * - A TypeBounds value, indicating new constraint bounds - * - Another type, indicating a solution for the parameter - * - * @pre `this contains param`. - */ - def updateEntry(param: PolyParam, tp: Type)(implicit ctx: Context): This - - /** A constraint that includes the relationship `p1 <: p2`. - * `<:` relationships between parameters ("edges") are propagated, but - * non-parameter bounds are left alone. - */ - def addLess(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This - - /** A constraint resulting from adding p2 = p1 to this constraint, and at the same - * time transferring all bounds of p2 to p1 - */ - def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This - - /** A new constraint which is derived from this constraint by removing - * the type parameter `param` from the domain and replacing all top-level occurrences - * of the parameter elsewhere in the constraint by type `tp`, or a conservative - * approximation of it if that is needed to avoid cycles. - * Occurrences nested inside a refinement or prefix are not affected. - */ - def replace(param: PolyParam, tp: Type)(implicit ctx: Context): This - - /** Narrow one of the bounds of type parameter `param` - * If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure - * that `param >: bound`. - */ - def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This - - /** Is entry associated with `pt` removable? This is the case if - * all type parameters of the entry are associated with type variables - * which have their `inst` fields set. - */ - def isRemovable(pt: PolyType): Boolean - - /** A new constraint with all entries coming from `pt` removed. */ - def remove(pt: PolyType)(implicit ctx: Context): This - - /** The polytypes constrained by this constraint */ - def domainPolys: List[PolyType] - - /** The polytype parameters constrained by this constraint */ - def domainParams: List[PolyParam] - - /** Check whether predicate holds for all parameters in constraint */ - def forallParams(p: PolyParam => Boolean): Boolean - - /** Perform operation `op` on all typevars, or only on uninstantiated - * typevars, depending on whether `uninstOnly` is set or not. - */ - def foreachTypeVar(op: TypeVar => Unit): Unit - - /** The uninstantiated typevars of this constraint */ - def uninstVars: collection.Seq[TypeVar] - - /** The weakest constraint that subsumes both this constraint and `other` */ - def & (other: Constraint)(implicit ctx: Context): Constraint - - /** Check that no constrained parameter contains itself as a bound */ - def checkNonCyclic()(implicit ctx: Context): Unit - - /** Check that constraint only refers to PolyParams bound by itself */ - def checkClosed()(implicit ctx: Context): Unit -} diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala deleted file mode 100644 index 0e155b9e1..000000000 --- a/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ /dev/null @@ -1,458 +0,0 @@ -package dotty.tools -package dotc -package core - -import Types._, Contexts._, Symbols._ -import Decorators._ -import config.Config -import config.Printers.{constr, typr} -import TypeApplications.EtaExpansion -import collection.mutable - -/** Methods for adding constraints and solving them. - * - * What goes into a Constraint as opposed to a ConstrainHandler? - * - * Constraint code is purely functional: Operations get constraints and produce new ones. - * Constraint code does not have access to a type-comparer. Anything regarding lubs and glbs has to be done - * elsewhere. - * - * By comparison: Constraint handlers are parts of type comparers and can use their functionality. - * Constraint handlers update the current constraint as a side effect. - */ -trait ConstraintHandling { - - implicit val ctx: Context - - protected def isSubType(tp1: Type, tp2: Type): Boolean - protected def isSameType(tp1: Type, tp2: Type): Boolean - - val state: TyperState - import state.constraint - - private var addConstraintInvocations = 0 - - /** If the constraint is frozen we cannot add new bounds to the constraint. */ - protected var frozenConstraint = false - - protected var alwaysFluid = false - - /** Perform `op` in a mode where all attempts to set `frozen` to true are ignored */ - def fluidly[T](op: => T): T = { - val saved = alwaysFluid - alwaysFluid = true - try op finally alwaysFluid = saved - } - - /** We are currently comparing polytypes. Used as a flag for - * optimization: when `false`, no need to do an expensive `pruneLambdaParams` - */ - protected var comparedPolyTypes: Set[PolyType] = Set.empty - - private def addOneBound(param: PolyParam, bound: Type, isUpper: Boolean): Boolean = - !constraint.contains(param) || { - def occursIn(bound: Type): Boolean = { - val b = bound.dealias - (b eq param) || { - b match { - case b: AndOrType => occursIn(b.tp1) || occursIn(b.tp2) - case b: TypeVar => occursIn(b.origin) - case _ => false - } - } - } - if (Config.checkConstraintsSeparated) - assert(!occursIn(bound), s"$param occurs in $bound") - val c1 = constraint.narrowBound(param, bound, isUpper) - (c1 eq constraint) || { - constraint = c1 - val TypeBounds(lo, hi) = constraint.entry(param) - isSubType(lo, hi) - } - } - - protected def addUpperBound(param: PolyParam, bound: Type): Boolean = { - def description = i"constraint $param <: $bound to\n$constraint" - if (bound.isRef(defn.NothingClass) && ctx.typerState.isGlobalCommittable) { - def msg = s"!!! instantiated to Nothing: $param, constraint = ${constraint.show}" - if (Config.failOnInstantiationToNothing) assert(false, msg) - else ctx.log(msg) - } - constr.println(i"adding $description") - val lower = constraint.lower(param) - val res = - addOneBound(param, bound, isUpper = true) && - lower.forall(addOneBound(_, bound, isUpper = true)) - constr.println(i"added $description = $res") - res - } - - protected def addLowerBound(param: PolyParam, bound: Type): Boolean = { - def description = i"constraint $param >: $bound to\n$constraint" - constr.println(i"adding $description") - val upper = constraint.upper(param) - val res = - addOneBound(param, bound, isUpper = false) && - upper.forall(addOneBound(_, bound, isUpper = false)) - constr.println(i"added $description = $res") - res - } - - protected def addLess(p1: PolyParam, p2: PolyParam): Boolean = { - def description = i"ordering $p1 <: $p2 to\n$constraint" - val res = - if (constraint.isLess(p2, p1)) unify(p2, p1) - else { - val down1 = p1 :: constraint.exclusiveLower(p1, p2) - val up2 = p2 :: constraint.exclusiveUpper(p2, p1) - val lo1 = constraint.nonParamBounds(p1).lo - val hi2 = constraint.nonParamBounds(p2).hi - constr.println(i"adding $description down1 = $down1, up2 = $up2") - constraint = constraint.addLess(p1, p2) - down1.forall(addOneBound(_, hi2, isUpper = true)) && - up2.forall(addOneBound(_, lo1, isUpper = false)) - } - constr.println(i"added $description = $res") - res - } - - /** Make p2 = p1, transfer all bounds of p2 to p1 - * @pre less(p1)(p2) - */ - private def unify(p1: PolyParam, p2: PolyParam): Boolean = { - constr.println(s"unifying $p1 $p2") - assert(constraint.isLess(p1, p2)) - val down = constraint.exclusiveLower(p2, p1) - val up = constraint.exclusiveUpper(p1, p2) - constraint = constraint.unify(p1, p2) - val bounds = constraint.nonParamBounds(p1) - val lo = bounds.lo - val hi = bounds.hi - isSubType(lo, hi) && - down.forall(addOneBound(_, hi, isUpper = true)) && - up.forall(addOneBound(_, lo, isUpper = false)) - } - - final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { - val saved = frozenConstraint - frozenConstraint = !alwaysFluid - try isSubType(tp1, tp2) - finally frozenConstraint = saved - } - - final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { - val saved = frozenConstraint - frozenConstraint = !alwaysFluid - try isSameType(tp1, tp2) - finally frozenConstraint = saved - } - - /** Test whether the lower bounds of all parameters in this - * constraint are a solution to the constraint. - */ - protected final def isSatisfiable: Boolean = - constraint.forallParams { param => - val TypeBounds(lo, hi) = constraint.entry(param) - isSubType(lo, hi) || { - ctx.log(i"sub fail $lo <:< $hi") - false - } - } - - /** Solve constraint set for given type parameter `param`. - * If `fromBelow` is true the parameter is approximated by its lower bound, - * otherwise it is approximated by its upper bound. However, any occurrences - * of the parameter in a refinement somewhere in the bound are removed. Also - * wildcard types in bounds are approximated by their upper or lower bounds. - * (Such occurrences can arise for F-bounded types). - * The constraint is left unchanged. - * @return the instantiating type - * @pre `param` is in the constraint's domain. - */ - final def approximation(param: PolyParam, fromBelow: Boolean): Type = { - val avoidParam = new TypeMap { - override def stopAtStatic = true - def apply(tp: Type) = mapOver { - tp match { - case tp: RefinedType if param occursIn tp.refinedInfo => tp.parent - case tp: WildcardType => - val bounds = tp.optBounds.orElse(TypeBounds.empty).bounds - // Try to instantiate the wildcard to a type that is known to conform to it. - // This means: - // If fromBelow is true, we minimize the type overall - // Hence, if variance < 0, pick the maximal safe type: bounds.lo - // (i.e. the whole bounds range is over the type) - // if variance > 0, pick the minimal safe type: bounds.hi - // (i.e. the whole bounds range is under the type) - // if variance == 0, pick bounds.lo anyway (this is arbitrary but in line with - // the principle that we pick the smaller type when in doubt). - // If fromBelow is false, we maximize the type overall and reverse the bounds - // if variance != 0. For variance == 0, we still minimize. - // In summary we pick the bound given by this table: - // - // variance | -1 0 1 - // ------------------------ - // from below | lo lo hi - // from above | hi lo lo - // - if (variance == 0 || fromBelow == (variance < 0)) bounds.lo else bounds.hi - case _ => tp - } - } - } - assert(constraint.contains(param)) - val bound = if (fromBelow) constraint.fullLowerBound(param) else constraint.fullUpperBound(param) - val inst = avoidParam(bound) - typr.println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}") - inst - } - - /** The instance type of `param` in the current constraint (which contains `param`). - * If `fromBelow` is true, the instance type is the lub of the parameter's - * lower bounds; otherwise it is the glb of its upper bounds. However, - * a lower bound instantiation can be a singleton type only if the upper bound - * is also a singleton type. - */ - def instanceType(param: PolyParam, fromBelow: Boolean): Type = { - def upperBound = constraint.fullUpperBound(param) - def isSingleton(tp: Type): Boolean = tp match { - case tp: SingletonType => true - case AndType(tp1, tp2) => isSingleton(tp1) | isSingleton(tp2) - case OrType(tp1, tp2) => isSingleton(tp1) & isSingleton(tp2) - case _ => false - } - def isFullyDefined(tp: Type): Boolean = tp match { - case tp: TypeVar => tp.isInstantiated && isFullyDefined(tp.instanceOpt) - case tp: TypeProxy => isFullyDefined(tp.underlying) - case tp: AndOrType => isFullyDefined(tp.tp1) && isFullyDefined(tp.tp2) - case _ => true - } - def isOrType(tp: Type): Boolean = tp.stripTypeVar.dealias match { - case tp: OrType => true - case tp: RefinedOrRecType => isOrType(tp.parent) - case AndType(tp1, tp2) => isOrType(tp1) | isOrType(tp2) - case WildcardType(bounds: TypeBounds) => isOrType(bounds.hi) - case _ => false - } - - // First, solve the constraint. - var inst = approximation(param, fromBelow) - - // Then, approximate by (1.) - (3.) and simplify as follows. - // 1. If instance is from below and is a singleton type, yet - // upper bound is not a singleton type, widen the instance. - if (fromBelow && isSingleton(inst) && !isSingleton(upperBound)) - inst = inst.widen - - inst = inst.simplified - - // 2. If instance is from below and is a fully-defined union type, yet upper bound - // is not a union type, approximate the union type from above by an intersection - // of all common base types. - if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound)) - inst = ctx.harmonizeUnion(inst) - - // 3. If instance is from below, and upper bound has open named parameters - // make sure the instance has all named parameters of the bound. - if (fromBelow) inst = inst.widenToNamedTypeParams(param.namedTypeParams) - inst - } - - /** Constraint `c1` subsumes constraint `c2`, if under `c2` as constraint we have - * for all poly params `p` defined in `c2` as `p >: L2 <: U2`: - * - * c1 defines p with bounds p >: L1 <: U1, and - * L2 <: L1, and - * U1 <: U2 - * - * Both `c1` and `c2` are required to derive from constraint `pre`, possibly - * narrowing it with further bounds. - */ - protected final def subsumes(c1: Constraint, c2: Constraint, pre: Constraint): Boolean = - if (c2 eq pre) true - else if (c1 eq pre) false - else { - val saved = constraint - try - c2.forallParams(p => - c1.contains(p) && - c2.upper(p).forall(c1.isLess(p, _)) && - isSubTypeWhenFrozen(c1.nonParamBounds(p), c2.nonParamBounds(p))) - finally constraint = saved - } - - /** The current bounds of type parameter `param` */ - final def bounds(param: PolyParam): TypeBounds = { - val e = constraint.entry(param) - if (e.exists) e.bounds else param.binder.paramBounds(param.paramNum) - } - - /** Add polytype `pt`, possibly with type variables `tvars`, to current constraint - * and propagate all bounds. - * @param tvars See Constraint#add - */ - def addToConstraint(pt: PolyType, tvars: List[TypeVar]): Unit = - assert { - checkPropagated(i"initialized $pt") { - constraint = constraint.add(pt, tvars) - pt.paramNames.indices.forall { i => - val param = PolyParam(pt, i) - val bounds = constraint.nonParamBounds(param) - val lower = constraint.lower(param) - val upper = constraint.upper(param) - if (lower.nonEmpty && !bounds.lo.isRef(defn.NothingClass) || - upper.nonEmpty && !bounds.hi.isRef(defn.AnyClass)) constr.println(i"INIT*** $pt") - lower.forall(addOneBound(_, bounds.hi, isUpper = true)) && - upper.forall(addOneBound(_, bounds.lo, isUpper = false)) - } - } - } - - /** Can `param` be constrained with new bounds? */ - final def canConstrain(param: PolyParam): Boolean = - !frozenConstraint && (constraint contains param) - - /** Add constraint `param <: bound` if `fromBelow` is false, `param >: bound` otherwise. - * `bound` is assumed to be in normalized form, as specified in `firstTry` and - * `secondTry` of `TypeComparer`. In particular, it should not be an alias type, - * lazy ref, typevar, wildcard type, error type. In addition, upper bounds may - * not be AndTypes and lower bounds may not be OrTypes. This is assured by the - * way isSubType is organized. - */ - protected def addConstraint(param: PolyParam, bound: Type, fromBelow: Boolean): Boolean = { - def description = i"constr $param ${if (fromBelow) ">:" else "<:"} $bound:\n$constraint" - //checkPropagated(s"adding $description")(true) // DEBUG in case following fails - checkPropagated(s"added $description") { - addConstraintInvocations += 1 - - /** When comparing lambdas we might get constraints such as - * `A <: X0` or `A = List[X0]` where `A` is a constrained parameter - * and `X0` is a lambda parameter. The constraint for `A` is not allowed - * to refer to such a lambda parameter because the lambda parameter is - * not visible where `A` is defined. Consequently, we need to - * approximate the bound so that the lambda parameter does not appear in it. - * If `tp` is an upper bound, we need to approximate with something smaller, - * otherwise something larger. - * Test case in pos/i94-nada.scala. This test crashes with an illegal instance - * error in Test2 when the rest of the SI-2712 fix is applied but `pruneLambdaParams` is - * missing. - */ - def pruneLambdaParams(tp: Type) = - if (comparedPolyTypes.nonEmpty) { - val approx = new ApproximatingTypeMap { - def apply(t: Type): Type = t match { - case t @ PolyParam(pt: PolyType, n) if comparedPolyTypes contains pt => - val effectiveVariance = if (fromBelow) -variance else variance - val bounds = pt.paramBounds(n) - if (effectiveVariance > 0) bounds.lo - else if (effectiveVariance < 0) bounds.hi - else NoType - case _ => - mapOver(t) - } - } - approx(tp) - } - else tp - - def addParamBound(bound: PolyParam) = - if (fromBelow) addLess(bound, param) else addLess(param, bound) - - /** Drop all constrained parameters that occur at the toplevel in `bound` and - * handle them by `addLess` calls. - * The preconditions make sure that such parameters occur only - * in one of two ways: - * - * 1. - * - * P <: Ts1 | ... | Tsm (m > 0) - * Tsi = T1 & ... Tn (n >= 0) - * Some of the Ti are constrained parameters - * - * 2. - * - * Ts1 & ... & Tsm <: P (m > 0) - * Tsi = T1 | ... | Tn (n >= 0) - * Some of the Ti are constrained parameters - * - * In each case we cannot leave the parameter in place, - * because that would risk making a parameter later a subtype or supertype - * of a bound where the parameter occurs again at toplevel, which leads to cycles - * in the subtyping test. So we intentionally narrow the constraint by - * recording an isLess relationship instead (even though this is not implied - * by the bound). - * - * Narrowing a constraint is better than widening it, because narrowing leads - * to incompleteness (which we face anyway, see for instance eitherIsSubType) - * but widening leads to unsoundness. - * - * A test case that demonstrates the problem is i864.scala. - * Turn Config.checkConstraintsSeparated on to get an accurate diagnostic - * of the cycle when it is created. - * - * @return The pruned type if all `addLess` calls succeed, `NoType` otherwise. - */ - def prune(bound: Type): Type = bound match { - case bound: AndOrType => - val p1 = prune(bound.tp1) - val p2 = prune(bound.tp2) - if (p1.exists && p2.exists) bound.derivedAndOrType(p1, p2) - else NoType - case bound: TypeVar if constraint contains bound.origin => - prune(bound.underlying) - case bound: PolyParam => - constraint.entry(bound) match { - case NoType => pruneLambdaParams(bound) - case _: TypeBounds => - if (!addParamBound(bound)) NoType - else if (fromBelow) defn.NothingType - else defn.AnyType - case inst => - prune(inst) - } - case _ => - pruneLambdaParams(bound) - } - - try bound match { - case bound: PolyParam if constraint contains bound => - addParamBound(bound) - case _ => - val pbound = prune(bound) - pbound.exists && ( - if (fromBelow) addLowerBound(param, pbound) else addUpperBound(param, pbound)) - } - finally addConstraintInvocations -= 1 - } - } - - /** Instantiate `param` to `tp` if the constraint stays satisfiable */ - protected def tryInstantiate(param: PolyParam, tp: Type): Boolean = { - val saved = constraint - constraint = - if (addConstraint(param, tp, fromBelow = true) && - addConstraint(param, tp, fromBelow = false)) constraint.replace(param, tp) - else saved - constraint ne saved - } - - /** Check that constraint is fully propagated. See comment in Config.checkConstraintsPropagated */ - def checkPropagated(msg: => String)(result: Boolean): Boolean = { - if (Config.checkConstraintsPropagated && result && addConstraintInvocations == 0) { - val saved = frozenConstraint - frozenConstraint = true - for (p <- constraint.domainParams) { - def check(cond: => Boolean, q: PolyParam, ordering: String, explanation: String): Unit = - assert(cond, i"propagation failure for $p $ordering $q: $explanation\n$msg") - for (u <- constraint.upper(p)) - check(bounds(p).hi <:< bounds(u).hi, u, "<:", "upper bound not propagated") - for (l <- constraint.lower(p)) { - check(bounds(l).lo <:< bounds(p).hi, l, ">:", "lower bound not propagated") - check(constraint.isLess(l, p), l, ">:", "reverse ordering (<:) missing") - } - } - frozenConstraint = saved - } - result - } -} diff --git a/src/dotty/tools/dotc/core/ConstraintRunInfo.scala b/src/dotty/tools/dotc/core/ConstraintRunInfo.scala deleted file mode 100644 index e0f659cc6..000000000 --- a/src/dotty/tools/dotc/core/ConstraintRunInfo.scala +++ /dev/null @@ -1,17 +0,0 @@ -package dotty.tools.dotc -package core - -import Contexts._ -import config.Printers.typr - -trait ConstraintRunInfo { self: RunInfo => - private var maxSize = 0 - private var maxConstraint: Constraint = _ - def recordConstraintSize(c: Constraint, size: Int) = - if (size > maxSize) { - maxSize = size - maxConstraint = c - } - def printMaxConstraint()(implicit ctx: Context) = - if (maxSize > 0) typr.println(s"max constraint = ${maxConstraint.show}") -} diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala deleted file mode 100644 index 639c4d111..000000000 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ /dev/null @@ -1,709 +0,0 @@ -package dotty.tools -package dotc -package core - -import interfaces.CompilerCallback -import Decorators._ -import Periods._ -import Names._ -import Phases._ -import Types._ -import Symbols._ -import Scopes._ -import NameOps._ -import Uniques._ -import SymDenotations._ -import Comments._ -import Flags.ParamAccessor -import util.Positions._ -import ast.Trees._ -import ast.untpd -import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource} -import typer.{Implicits, ImplicitRunInfo, ImportInfo, Inliner, NamerContextOps, SearchHistory, TypeAssigner, Typer} -import Implicits.ContextualImplicits -import config.Settings._ -import config.Config -import reporting._ -import collection.mutable -import collection.immutable.BitSet -import printing._ -import config.{Settings, ScalaSettings, Platform, JavaPlatform} -import language.implicitConversions -import DenotTransformers.DenotTransformer -import util.Property.Key -import xsbti.AnalysisCallback - -object Contexts { - - /** A context is passed basically everywhere in dotc. - * This is convenient but carries the risk of captured contexts in - * objects that turn into space leaks. To combat this risk, here are some - * conventions to follow: - * - * - Never let an implicit context be an argument of a class whose instances - * live longer than the context. - * - Classes that need contexts for their initialization take an explicit parameter - * named `initctx`. They pass initctx to all positions where it is needed - * (and these positions should all be part of the intialization sequence of the class). - * - Classes that need contexts that survive initialization are instead passed - * a "condensed context", typically named `cctx` (or they create one). Condensed contexts - * just add some basic information to the context base without the - * risk of capturing complete trees. - * - To make sure these rules are kept, it would be good to do a sanity - * check using bytecode inspection with javap or scalap: Keep track - * of all class fields of type context; allow them only in whitelisted - * classes (which should be short-lived). - */ - abstract class Context extends Periods - with Substituters - with TypeOps - with Phases - with Printers - with Symbols - with SymDenotations - with Reporting - with NamerContextOps - with Cloneable { thiscontext => - implicit def ctx: Context = this - - /** The context base at the root */ - val base: ContextBase - - /** All outer contexts, ending in `base.initialCtx` and then `NoContext` */ - def outersIterator = new Iterator[Context] { - var current = thiscontext - def hasNext = current != NoContext - def next = { val c = current; current = current.outer; c } - } - - /** The outer context */ - private[this] var _outer: Context = _ - protected def outer_=(outer: Context) = _outer = outer - def outer: Context = _outer - - /** The compiler callback implementation, or null if no callback will be called. */ - private[this] var _compilerCallback: CompilerCallback = _ - protected def compilerCallback_=(callback: CompilerCallback) = - _compilerCallback = callback - def compilerCallback: CompilerCallback = _compilerCallback - - /** The sbt callback implementation if we are run from sbt, null otherwise */ - private[this] var _sbtCallback: AnalysisCallback = _ - protected def sbtCallback_=(callback: AnalysisCallback) = - _sbtCallback = callback - def sbtCallback: AnalysisCallback = _sbtCallback - - /** The current context */ - private[this] var _period: Period = _ - protected def period_=(period: Period) = { - assert(period.firstPhaseId == period.lastPhaseId, period) - _period = period - } - def period: Period = _period - - /** The scope nesting level */ - private[this] var _mode: Mode = _ - protected def mode_=(mode: Mode) = _mode = mode - def mode: Mode = _mode - - /** The current type comparer */ - private[this] var _typerState: TyperState = _ - protected def typerState_=(typerState: TyperState) = _typerState = typerState - def typerState: TyperState = _typerState - - /** The current plain printer */ - private[this] var _printerFn: Context => Printer = _ - protected def printerFn_=(printerFn: Context => Printer) = _printerFn = printerFn - def printerFn: Context => Printer = _printerFn - - /** The current owner symbol */ - private[this] var _owner: Symbol = _ - protected def owner_=(owner: Symbol) = _owner = owner - def owner: Symbol = _owner - - /** The current settings values */ - private[this] var _sstate: SettingsState = _ - protected def sstate_=(sstate: SettingsState) = _sstate = sstate - def sstate: SettingsState = _sstate - - /** The current tree */ - private[this] var _compilationUnit: CompilationUnit = _ - protected def compilationUnit_=(compilationUnit: CompilationUnit) = _compilationUnit = compilationUnit - def compilationUnit: CompilationUnit = _compilationUnit - - /** The current tree */ - private[this] var _tree: Tree[_ >: Untyped]= _ - protected def tree_=(tree: Tree[_ >: Untyped]) = _tree = tree - def tree: Tree[_ >: Untyped] = _tree - - /** The current scope */ - private[this] var _scope: Scope = _ - protected def scope_=(scope: Scope) = _scope = scope - def scope: Scope = _scope - - /** The current type assigner or typer */ - private[this] var _typeAssigner: TypeAssigner = _ - protected def typeAssigner_=(typeAssigner: TypeAssigner) = _typeAssigner = typeAssigner - def typeAssigner: TypeAssigner = _typeAssigner - def typer: Typer = _typeAssigner.asInstanceOf[Typer] - - /** The currently active import info */ - private[this] var _importInfo: ImportInfo = _ - protected def importInfo_=(importInfo: ImportInfo) = _importInfo = importInfo - def importInfo: ImportInfo = _importInfo - - /** The current compiler-run specific Info */ - private[this] var _runInfo: RunInfo = _ - protected def runInfo_=(runInfo: RunInfo) = _runInfo = runInfo - def runInfo: RunInfo = _runInfo - - /** An optional diagostics buffer than is used by some checking code - * to provide more information in the buffer if it exists. - */ - private var _diagnostics: Option[StringBuilder] = _ - protected def diagnostics_=(diagnostics: Option[StringBuilder]) = _diagnostics = diagnostics - def diagnostics: Option[StringBuilder] = _diagnostics - - /** The current bounds in force for type parameters appearing in a GADT */ - private var _gadt: GADTMap = _ - protected def gadt_=(gadt: GADTMap) = _gadt = gadt - def gadt: GADTMap = _gadt - - /**The current fresh name creator */ - private[this] var _freshNames: FreshNameCreator = _ - protected def freshNames_=(freshNames: FreshNameCreator) = _freshNames = freshNames - def freshNames: FreshNameCreator = _freshNames - - def freshName(prefix: String = ""): String = freshNames.newName(prefix) - def freshName(prefix: Name): String = freshName(prefix.toString) - - /** A map in which more contextual properties can be stored */ - private var _moreProperties: Map[Key[Any], Any] = _ - protected def moreProperties_=(moreProperties: Map[Key[Any], Any]) = _moreProperties = moreProperties - def moreProperties: Map[Key[Any], Any] = _moreProperties - - def property[T](key: Key[T]): Option[T] = - moreProperties.get(key).asInstanceOf[Option[T]] - - private var _typeComparer: TypeComparer = _ - protected def typeComparer_=(typeComparer: TypeComparer) = _typeComparer = typeComparer - def typeComparer: TypeComparer = { - if (_typeComparer.ctx ne this) - _typeComparer = _typeComparer.copyIn(this) - _typeComparer - } - - /** Number of findMember calls on stack */ - private[core] var findMemberCount: Int = 0 - - /** List of names which have a findMemberCall on stack, - * after Config.LogPendingFindMemberThreshold is reached. - */ - private[core] var pendingMemberSearches: List[Name] = Nil - - /** The new implicit references that are introduced by this scope */ - private var implicitsCache: ContextualImplicits = null - def implicits: ContextualImplicits = { - if (implicitsCache == null ) - implicitsCache = { - val implicitRefs: List[TermRef] = - if (isClassDefContext) - try owner.thisType.implicitMembers - catch { - case ex: CyclicReference => Nil - } - else if (isImportContext) importInfo.importedImplicits - else if (isNonEmptyScopeContext) scope.implicitDecls - else Nil - val outerImplicits = - if (isImportContext && importInfo.hiddenRoot.exists) - outer.implicits exclude importInfo.hiddenRoot - else - outer.implicits - if (implicitRefs.isEmpty) outerImplicits - else new ContextualImplicits(implicitRefs, outerImplicits)(this) - } - implicitsCache - } - - /** The history of implicit searches that are currently active */ - private var _searchHistory: SearchHistory = null - protected def searchHistory_= (searchHistory: SearchHistory) = _searchHistory = searchHistory - def searchHistory: SearchHistory = _searchHistory - - /** Those fields are used to cache phases created in withPhase. - * phasedCtx is first phase with altered phase ever requested. - * phasedCtxs is array that uses phaseId's as indexes, - * contexts are created only on request and cached in this array - */ - private var phasedCtx: Context = _ - private var phasedCtxs: Array[Context] = _ - - /** This context at given phase. - * This method will always return a phase period equal to phaseId, thus will never return squashed phases - */ - final def withPhase(phaseId: PhaseId): Context = - if (this.phaseId == phaseId) this - else if (phasedCtx.phaseId == phaseId) phasedCtx - else if (phasedCtxs != null && phasedCtxs(phaseId) != null) phasedCtxs(phaseId) - else { - val ctx1 = fresh.setPhase(phaseId) - if (phasedCtx eq this) phasedCtx = ctx1 - else { - if (phasedCtxs == null) phasedCtxs = new Array[Context](base.phases.length) - phasedCtxs(phaseId) = ctx1 - } - ctx1 - } - - final def withPhase(phase: Phase): Context = - withPhase(phase.id) - - final def withPhaseNoLater(phase: Phase) = - if (phase.exists && ctx.phase.id > phase.id) withPhase(phase) else ctx - - /** If -Ydebug is on, the top of the stack trace where this context - * was created, otherwise `null`. - */ - private var creationTrace: Array[StackTraceElement] = _ - - private def setCreationTrace() = - if (this.settings.YtraceContextCreation.value) - creationTrace = (new Throwable).getStackTrace().take(20) - - /** Print all enclosing context's creation stacktraces */ - def printCreationTraces() = { - println("=== context creation trace =======") - for (ctx <- outersIterator) { - println(s">>>>>>>>> $ctx") - if (ctx.creationTrace != null) println(ctx.creationTrace.mkString("\n")) - } - println("=== end context creation trace ===") - } - - /** The current reporter */ - def reporter: Reporter = typerState.reporter - - /** Is this a context for the members of a class definition? */ - def isClassDefContext: Boolean = - owner.isClass && (owner ne outer.owner) - - /** Is this a context that introduces an import clause? */ - def isImportContext: Boolean = - (this ne NoContext) && (this.importInfo ne outer.importInfo) - - /** Is this a context that introduces a non-empty scope? */ - def isNonEmptyScopeContext: Boolean = - (this.scope ne outer.scope) && this.scope.nonEmpty - - /** Leave message in diagnostics buffer if it exists */ - def diagnose(str: => String) = - for (sb <- diagnostics) { - sb.setLength(0) - sb.append(str) - } - - /** The next outer context whose tree is a template or package definition */ - def enclTemplate: Context = { - var c = this - while (c != NoContext && !c.tree.isInstanceOf[Template[_]] && !c.tree.isInstanceOf[PackageDef[_]]) - c = c.outer - c - } - - /** The context for a supercall. This context is used for elaborating - * the parents of a class and their arguments. - * The context is computed from the current class context. It has - * - * - as owner: The primary constructor of the class - * - as outer context: The context enclosing the class context - * - as scope: The parameter accessors in the class context - * - with additional mode: InSuperCall - * - * The reasons for this peculiar choice of attributes are as follows: - * - * - The constructor must be the owner, because that's where any local methods or closures - * should go. - * - The context may not see any class members (inherited or defined), and should - * instead see definitions defined in the outer context which might be shadowed by - * such class members. That's why the outer context must be the outer context of the class. - * - At the same time the context should see the parameter accessors of the current class, - * that's why they get added to the local scope. An alternative would have been to have the - * context see the constructor parameters instead, but then we'd need a final substitution step - * from constructor parameters to class parameter accessors. - */ - def superCallContext: Context = { - val locals = newScopeWith(owner.asClass.paramAccessors: _*) - superOrThisCallContext(owner.primaryConstructor, locals) - } - - /** The context for the arguments of a this(...) constructor call. - * The context is computed from the local auxiliary constructor context. - * It has - * - * - as owner: The auxiliary constructor - * - as outer context: The context enclosing the enclosing class context - * - as scope: The parameters of the auxiliary constructor. - */ - def thisCallArgContext: Context = { - assert(owner.isClassConstructor) - val constrCtx = outersIterator.dropWhile(_.outer.owner == owner).next - superOrThisCallContext(owner, constrCtx.scope) - .setTyperState(typerState) - .setGadt(gadt) - } - - /** The super- or this-call context with given owner and locals. */ - private def superOrThisCallContext(owner: Symbol, locals: Scope): FreshContext = { - var classCtx = outersIterator.dropWhile(!_.isClassDefContext).next - classCtx.outer.fresh.setOwner(owner) - .setScope(locals) - .setMode(classCtx.mode | Mode.InSuperCall) - } - - /** The context of expression `expr` seen as a member of a statement sequence */ - def exprContext(stat: Tree[_ >: Untyped], exprOwner: Symbol) = - if (exprOwner == this.owner) this - else if (untpd.isSuperConstrCall(stat) && this.owner.isClass) superCallContext - else ctx.fresh.setOwner(exprOwner) - - /** The current source file; will be derived from current - * compilation unit. - */ - def source: SourceFile = - if (compilationUnit == null) NoSource else compilationUnit.source - - /** Does current phase use an erased types interpretation? */ - def erasedTypes: Boolean = phase.erasedTypes - - /** Is the debug option set? */ - def debug: Boolean = base.settings.debug.value - - /** Is the verbose option set? */ - def verbose: Boolean = base.settings.verbose.value - - /** Should use colors when printing? */ - def useColors: Boolean = - base.settings.color.value == "always" - - /** A condensed context containing essential information of this but - * no outer contexts except the initial context. - private var _condensed: CondensedContext = null - def condensed: CondensedContext = { - if (_condensed eq outer.condensed) - _condensed = base.initialCtx.fresh - .withPeriod(period) - .withNewMode(mode) - // typerState and its constraint is not preserved in condensed - // reporter is always ThrowingReporter - .withPrinterFn(printerFn) - .withOwner(owner) - .withSettings(sstate) - // tree is not preserved in condensed - .withRunInfo(runInfo) - .withDiagnostics(diagnostics) - .withMoreProperties(moreProperties) - _condensed - } - */ - - protected def init(outer: Context): this.type = { - this.outer = outer - this.implicitsCache = null - this.phasedCtx = this - this.phasedCtxs = null - setCreationTrace() - this - } - - /** A fresh clone of this context. */ - def fresh: FreshContext = clone.asInstanceOf[FreshContext].init(this) - - final def withOwner(owner: Symbol): Context = - if (owner ne this.owner) fresh.setOwner(owner) else this - - override def toString = - "Context(\n" + - (outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}") mkString "\n") - } - - /** A condensed context provides only a small memory footprint over - * a Context base, and therefore can be stored without problems in - * long-lived objects. - abstract class CondensedContext extends Context { - override def condensed = this - } - */ - - /** A fresh context allows selective modification - * of its attributes using the with... methods. - */ - abstract class FreshContext extends Context { - def setPeriod(period: Period): this.type = { this.period = period; this } - def setMode(mode: Mode): this.type = { this.mode = mode; this } - def setCompilerCallback(callback: CompilerCallback): this.type = { this.compilerCallback = callback; this } - def setSbtCallback(callback: AnalysisCallback): this.type = { this.sbtCallback = callback; this } - def setTyperState(typerState: TyperState): this.type = { this.typerState = typerState; this } - def setReporter(reporter: Reporter): this.type = setTyperState(typerState.withReporter(reporter)) - def setNewTyperState: this.type = setTyperState(typerState.fresh(isCommittable = true)) - def setExploreTyperState: this.type = setTyperState(typerState.fresh(isCommittable = false)) - def setPrinterFn(printer: Context => Printer): this.type = { this.printerFn = printer; this } - def setOwner(owner: Symbol): this.type = { assert(owner != NoSymbol); this.owner = owner; this } - def setSettings(sstate: SettingsState): this.type = { this.sstate = sstate; this } - def setCompilationUnit(compilationUnit: CompilationUnit): this.type = { this.compilationUnit = compilationUnit; this } - def setTree(tree: Tree[_ >: Untyped]): this.type = { this.tree = tree; this } - def setScope(scope: Scope): this.type = { this.scope = scope; this } - def setNewScope: this.type = { this.scope = newScope; this } - def setTypeAssigner(typeAssigner: TypeAssigner): this.type = { this.typeAssigner = typeAssigner; this } - def setTyper(typer: Typer): this.type = { this.scope = typer.scope; setTypeAssigner(typer) } - def setImportInfo(importInfo: ImportInfo): this.type = { this.importInfo = importInfo; this } - def setRunInfo(runInfo: RunInfo): this.type = { this.runInfo = runInfo; this } - def setDiagnostics(diagnostics: Option[StringBuilder]): this.type = { this.diagnostics = diagnostics; this } - def setGadt(gadt: GADTMap): this.type = { this.gadt = gadt; this } - def setTypeComparerFn(tcfn: Context => TypeComparer): this.type = { this.typeComparer = tcfn(this); this } - def setSearchHistory(searchHistory: SearchHistory): this.type = { this.searchHistory = searchHistory; this } - def setFreshNames(freshNames: FreshNameCreator): this.type = { this.freshNames = freshNames; this } - def setMoreProperties(moreProperties: Map[Key[Any], Any]): this.type = { this.moreProperties = moreProperties; this } - - def setProperty[T](key: Key[T], value: T): this.type = - setMoreProperties(moreProperties.updated(key, value)) - - def setPhase(pid: PhaseId): this.type = setPeriod(Period(runId, pid)) - def setPhase(phase: Phase): this.type = setPeriod(Period(runId, phase.start, phase.end)) - - def setSetting[T](setting: Setting[T], value: T): this.type = - setSettings(setting.updateIn(sstate, value)) - - def setFreshGADTBounds: this.type = { this.gadt = new GADTMap(gadt.bounds); this } - - def setDebug = setSetting(base.settings.debug, true) - } - - implicit class ModeChanges(val c: Context) extends AnyVal { - final def withModeBits(mode: Mode): Context = - if (mode != c.mode) c.fresh.setMode(mode) else c - - final def addMode(mode: Mode): Context = withModeBits(c.mode | mode) - final def maskMode(mode: Mode): Context = withModeBits(c.mode & mode) - final def retractMode(mode: Mode): Context = withModeBits(c.mode &~ mode) - } - - implicit class FreshModeChanges(val c: FreshContext) extends AnyVal { - final def addMode(mode: Mode): c.type = c.setMode(c.mode | mode) - final def maskMode(mode: Mode): c.type = c.setMode(c.mode & mode) - final def retractMode(mode: Mode): c.type = c.setMode(c.mode &~ mode) - } - - /** A class defining the initial context with given context base - * and set of possible settings. - */ - private class InitialContext(val base: ContextBase, settings: SettingGroup) extends FreshContext { - outer = NoContext - period = InitialPeriod - mode = Mode.None - typerState = new TyperState(new ConsoleReporter()) - printerFn = new RefinedPrinter(_) - owner = NoSymbol - sstate = settings.defaultState - tree = untpd.EmptyTree - typeAssigner = TypeAssigner - runInfo = new RunInfo(this) - diagnostics = None - freshNames = new FreshNameCreator.Default - moreProperties = Map.empty - typeComparer = new TypeComparer(this) - searchHistory = new SearchHistory(0, Map()) - gadt = new GADTMap(SimpleMap.Empty) - } - - @sharable object NoContext extends Context { - val base = null - override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null)(this) - } - - /** A context base defines state and associated methods that exist once per - * compiler run. - */ - class ContextBase extends ContextState - with Denotations.DenotationsBase - with Phases.PhasesBase { - - /** The applicable settings */ - val settings = new ScalaSettings - - /** The initial context */ - val initialCtx: Context = new InitialContext(this, settings) - - /** The symbol loaders */ - val loaders = new SymbolLoaders - - /** The platform, initialized by `initPlatform()`. */ - private var _platform: Platform = _ - - /** The platform */ - def platform: Platform = { - if (_platform == null) { - throw new IllegalStateException( - "initialize() must be called before accessing platform") - } - _platform - } - - protected def newPlatform(implicit ctx: Context): Platform = - new JavaPlatform - - /** The loader that loads the members of _root_ */ - def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root) - - // Set up some phases to get started */ - usePhases(List(SomePhase)) - - /** The standard definitions */ - val definitions = new Definitions - - /** Initializes the `ContextBase` with a starting context. - * This initializes the `platform` and the `definitions`. - */ - def initialize()(implicit ctx: Context): Unit = { - _platform = newPlatform - definitions.init() - } - - def squashed(p: Phase): Phase = { - allPhases.find(_.period.containsPhaseId(p.id)).getOrElse(NoPhase) - } - } - - /** The essential mutable state of a context base, collected into a common class */ - class ContextState { - // Symbols state - - /** A counter for unique ids */ - private[core] var _nextId = 0 - - def nextId = { _nextId += 1; _nextId } - - /** A map from a superclass id to the typeref of the class that has it */ - private[core] var classOfId = new Array[ClassSymbol](Config.InitialSuperIdsSize) - - /** A map from a the typeref of a class to its superclass id */ - private[core] val superIdOfClass = new mutable.AnyRefMap[ClassSymbol, Int] - - /** The last allocated superclass id */ - private[core] var lastSuperId = -1 - - /** Allocate and return next free superclass id */ - private[core] def nextSuperId: Int = { - lastSuperId += 1 - if (lastSuperId >= classOfId.length) { - val tmp = new Array[ClassSymbol](classOfId.length * 2) - classOfId.copyToArray(tmp) - classOfId = tmp - } - lastSuperId - } - - // Types state - /** A table for hash consing unique types */ - private[core] val uniques = new util.HashSet[Type](Config.initialUniquesCapacity) { - override def hash(x: Type): Int = x.hash - } - - /** A table for hash consing unique refined types */ - private[dotc] val uniqueRefinedTypes = new RefinedUniques - - /** A table for hash consing unique named types */ - private[core] val uniqueNamedTypes = new NamedTypeUniques - - /** A table for hash consing unique type bounds */ - private[core] val uniqueTypeAliases = new TypeAliasUniques - - private def uniqueSets = Map( - "uniques" -> uniques, - "uniqueRefinedTypes" -> uniqueRefinedTypes, - "uniqueNamedTypes" -> uniqueNamedTypes, - "uniqueTypeAliases" -> uniqueTypeAliases) - - /** A map that associates label and size of all uniques sets */ - def uniquesSizes: Map[String, Int] = uniqueSets.mapValues(_.size) - - /** The number of recursive invocation of underlying on a NamedType - * during a controlled operation. - */ - private[core] var underlyingRecursions: Int = 0 - - /** The set of named types on which a currently active invocation - * of underlying during a controlled operation exists. */ - private[core] val pendingUnderlying = new mutable.HashSet[Type] - - /** A flag that some unsafe nonvariant instantiation was encountered - * in this run. Used as a shortcut to a avoid scans of types in - * Typer.typedSelect. - */ - private[dotty] var unsafeNonvariant: RunId = NoRunId - - // Phases state - - private[core] var phasesPlan: List[List[Phase]] = _ - - /** Phases by id */ - private[core] var phases: Array[Phase] = _ - - /** Phases with consecutive Transforms grouped into a single phase, Empty array if squashing is disabled */ - private[core] var squashedPhases: Array[Phase] = Array.empty[Phase] - - /** Next denotation transformer id */ - private[core] var nextDenotTransformerId: Array[Int] = _ - - private[core] var denotTransformers: Array[DenotTransformer] = _ - - // Printers state - /** Number of recursive invocations of a show method on current stack */ - private[dotc] var toTextRecursions = 0 - - // Reporters state - private[dotc] var indent = 0 - - protected[dotc] val indentTab = " " - - def reset() = { - for ((_, set) <- uniqueSets) set.clear() - for (i <- 0 until classOfId.length) classOfId(i) = null - superIdOfClass.clear() - lastSuperId = -1 - } - - // Test that access is single threaded - - /** The thread on which `checkSingleThreaded was invoked last */ - @sharable private var thread: Thread = null - - /** Check that we are on the same thread as before */ - def checkSingleThreaded() = - if (thread == null) thread = Thread.currentThread() - else assert(thread == Thread.currentThread(), "illegal multithreaded access to ContextBase") - } - - object Context { - - /** Implicit conversion that injects all printer operations into a context */ - implicit def toPrinter(ctx: Context): Printer = ctx.printer - - /** implicit conversion that injects all ContextBase members into a context */ - implicit def toBase(ctx: Context): ContextBase = ctx.base - - // @sharable val theBase = new ContextBase // !!! DEBUG, so that we can use a minimal context for reporting even in code that normally cannot access a context - } - - /** Info that changes on each compiler run */ - class RunInfo(initctx: Context) extends ImplicitRunInfo with ConstraintRunInfo { - implicit val ctx: Context = initctx - } - - class GADTMap(initBounds: SimpleMap[Symbol, TypeBounds]) { - private var myBounds = initBounds - def setBounds(sym: Symbol, b: TypeBounds): Unit = - myBounds = myBounds.updated(sym, b) - def bounds = myBounds - } -} diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala deleted file mode 100644 index a105741f5..000000000 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ /dev/null @@ -1,185 +0,0 @@ -package dotty.tools.dotc -package core - -import annotation.tailrec -import Symbols._ -import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer, printing.Showable -import util.Positions.Position, util.SourcePosition -import collection.mutable.ListBuffer -import dotty.tools.dotc.transform.TreeTransforms._ -import ast.tpd._ -import scala.language.implicitConversions -import printing.Formatting._ - -/** This object provides useful implicit decorators for types defined elsewhere */ -object Decorators { - - /** Turns Strings into PreNames, adding toType/TermName methods */ - implicit class StringDecorator(val s: String) extends AnyVal with PreName { - def toTypeName: TypeName = typeName(s) - def toTermName: TermName = termName(s) - def toText(printer: Printer): Text = Str(s) - } - - /** Implements a findSymbol method on iterators of Symbols that - * works like find but avoids Option, replacing None with NoSymbol. - */ - implicit class SymbolIteratorDecorator(val it: Iterator[Symbol]) extends AnyVal { - final def findSymbol(p: Symbol => Boolean): Symbol = { - while (it.hasNext) { - val sym = it.next - if (p(sym)) return sym - } - NoSymbol - } - } - - final val MaxFilterRecursions = 1000 - - /** Implements filterConserve, zipWithConserve methods - * on lists that avoid duplication of list nodes where feasible. - */ - implicit class ListDecorator[T](val xs: List[T]) extends AnyVal { - - final def mapconserve[U](f: T => U): List[U] = { - @tailrec - def loop(mapped: ListBuffer[U], unchanged: List[U], pending: List[T]): List[U] = - if (pending.isEmpty) { - if (mapped eq null) unchanged - else mapped.prependToList(unchanged) - } else { - val head0 = pending.head - val head1 = f(head0) - - if (head1.asInstanceOf[AnyRef] eq head0.asInstanceOf[AnyRef]) - loop(mapped, unchanged, pending.tail) - else { - val b = if (mapped eq null) new ListBuffer[U] else mapped - var xc = unchanged - while (xc ne pending) { - b += xc.head - xc = xc.tail - } - b += head1 - val tail0 = pending.tail - loop(b, tail0.asInstanceOf[List[U]], tail0) - } - } - loop(null, xs.asInstanceOf[List[U]], xs) - } - - /** Like `xs filter p` but returns list `xs` itself - instead of a copy - - * if `p` is true for all elements and `xs` is not longer - * than `MaxFilterRecursions`. - */ - def filterConserve(p: T => Boolean): List[T] = { - def loop(xs: List[T], nrec: Int): List[T] = xs match { - case Nil => xs - case x :: xs1 => - if (nrec < MaxFilterRecursions) { - val ys1 = loop(xs1, nrec + 1) - if (p(x)) - if (ys1 eq xs1) xs else x :: ys1 - else - ys1 - } else xs filter p - } - loop(xs, 0) - } - - /** Like `(xs, ys).zipped.map(f)`, but returns list `xs` itself - * - instead of a copy - if function `f` maps all elements of - * `xs` to themselves. Also, it is required that `ys` is at least - * as long as `xs`. - */ - def zipWithConserve[U](ys: List[U])(f: (T, U) => T): List[T] = - if (xs.isEmpty) xs - else { - val x1 = f(xs.head, ys.head) - val xs1 = xs.tail.zipWithConserve(ys.tail)(f) - if ((x1.asInstanceOf[AnyRef] eq xs.head.asInstanceOf[AnyRef]) && - (xs1 eq xs.tail)) xs - else x1 :: xs1 - } - - def foldRightBN[U](z: => U)(op: (T, => U) => U): U = xs match { - case Nil => z - case x :: xs1 => op(x, xs1.foldRightBN(z)(op)) - } - - final def hasSameLengthAs[U](ys: List[U]): Boolean = { - @tailrec def loop(xs: List[T], ys: List[U]): Boolean = - if (xs.isEmpty) ys.isEmpty - else ys.nonEmpty && loop(xs.tail, ys.tail) - loop(xs, ys) - } - - /** Union on lists seen as sets */ - def | (ys: List[T]): List[T] = xs ++ (ys filterNot (xs contains _)) - - /** Intersection on lists seen as sets */ - def & (ys: List[T]): List[T] = xs filter (ys contains _) - } - - implicit class ListOfListDecorator[T](val xss: List[List[T]]) extends AnyVal { - def nestedMap[U](f: T => U): List[List[U]] = xss map (_ map f) - def nestedMapconserve[U](f: T => U): List[List[U]] = xss mapconserve (_ mapconserve f) - } - - implicit class TextToString(val text: Text) extends AnyVal { - def show(implicit ctx: Context) = text.mkString(ctx.settings.pageWidth.value) - } - - /** Test whether a list of strings representing phases contains - * a given phase. See [[config.CompilerCommand#explainAdvanced]] for the - * exact meaning of "contains" here. - */ - implicit class PhaseListDecorator(val names: List[String]) extends AnyVal { - def containsPhase(phase: Phase): Boolean = phase match { - case phase: TreeTransformer => phase.miniPhases.exists(containsPhase) - case _ => - names exists { name => - name == "all" || { - val strippedName = name.stripSuffix("+") - val logNextPhase = name ne strippedName - phase.phaseName.startsWith(strippedName) || - (logNextPhase && phase.prev.phaseName.startsWith(strippedName)) - } - } - } - } - - implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition = { - def recur(inlinedCalls: List[Tree], pos: Position): SourcePosition = inlinedCalls match { - case inlinedCall :: rest => - sourceFile(inlinedCall).atPos(pos).withOuter(recur(rest, inlinedCall.pos)) - case empty => - ctx.source.atPos(pos) - } - recur(enclosingInlineds, pos) - } - - implicit class StringInterpolators(val sc: StringContext) extends AnyVal { - - /** General purpose string formatting */ - def i(args: Any*)(implicit ctx: Context): String = - new StringFormatter(sc).assemble(args) - - /** Formatting for error messages: Like `i` but suppress follow-on - * error messages after the first one if some of their arguments are "non-sensical". - */ - def em(args: Any*)(implicit ctx: Context): String = - new ErrorMessageFormatter(sc).assemble(args) - - /** Formatting with added explanations: Like `em`, but add explanations to - * give more info about type variables and to disambiguate where needed. - */ - def ex(args: Any*)(implicit ctx: Context): String = - explained2(implicit ctx => em(args: _*)) - - /** Formatter that adds syntax highlighting to all interpolated values */ - def hl(args: Any*)(implicit ctx: Context): String = - new SyntaxFormatter(sc).assemble(args).stripMargin - } -} - diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala deleted file mode 100644 index 4b090d9b1..000000000 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ /dev/null @@ -1,807 +0,0 @@ -package dotty.tools -package dotc -package core - -import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._ -import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._, Periods._ -import unpickleScala2.Scala2Unpickler.ensureConstructor -import scala.annotation.{ switch, meta } -import scala.collection.{ mutable, immutable } -import PartialFunction._ -import collection.mutable -import scala.reflect.api.{ Universe => ApiUniverse } - -object Definitions { - val MaxTupleArity, MaxAbstractFunctionArity = 22 - val MaxFunctionArity = 30 - // Awaiting a definite solution that drops the limit altogether, 30 gives a safety - // margin over the previous 22, so that treecopiers in miniphases are allowed to - // temporarily create larger closures. This is needed in lambda lift where large closures - // are first formed by treecopiers before they are split apart into parameters and - // environment in the lambdalift transform itself. -} - -/** A class defining symbols and types of standard definitions - * - * Note: There's a much nicer design possible once we have implicit functions. - * The idea is explored to some degree in branch wip-definitions (#929): Instead of a type - * and a separate symbol definition, we produce in one line an implicit function from - * Context to Symbol, and possibly also the corresponding type. This cuts down on all - * the duplication encountered here. - * - * wip-definitions tries to do the same with an implicit conversion from a SymbolPerRun - * type to a symbol type. The problem with that is universal equality. Comparisons will - * not trigger the conversion and will therefore likely return false results. - * - * So the branch is put on hold, until we have implicit functions, which will always - * automatically be dereferenced. - */ -class Definitions { - import Definitions._ - - private implicit var ctx: Context = _ - - private def newSymbol[N <: Name](owner: Symbol, name: N, flags: FlagSet, info: Type) = - ctx.newSymbol(owner, name, flags | Permanent, info) - - private def newClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, infoFn: ClassSymbol => Type) = - ctx.newClassSymbol(owner, name, flags | Permanent, infoFn).entered - - private def newCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = - ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered - - private def newTopClassSymbol(name: TypeName, flags: FlagSet, parents: List[TypeRef]) = - completeClass(newCompleteClassSymbol(ScalaPackageClass, name, flags, parents)) - - private def newTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - scope.enter(newSymbol(cls, name, flags, TypeBounds.empty)) - - private def newTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - newTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) - - private def newSyntheticTypeParam(cls: ClassSymbol, scope: MutableScope, paramFlags: FlagSet, suffix: String = "T0") = - newTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope) - - // NOTE: Ideally we would write `parentConstrs: => Type*` but SIP-24 is only - // implemented in Dotty and not in Scala 2. - // See <http://docs.scala-lang.org/sips/pending/repeated-byname.html>. - private def specialPolyClass(name: TypeName, paramFlags: FlagSet, parentConstrs: => Seq[Type]): ClassSymbol = { - val completer = new LazyType { - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val cls = denot.asClass.classSymbol - val paramDecls = newScope - val typeParam = newSyntheticTypeParam(cls, paramDecls, paramFlags) - def instantiate(tpe: Type) = - if (tpe.typeParams.nonEmpty) tpe.appliedTo(typeParam.typeRef) - else tpe - val parents = parentConstrs.toList map instantiate - val parentRefs: List[TypeRef] = ctx.normalizeToClassRefs(parents, cls, paramDecls) - denot.info = ClassInfo(ScalaPackageClass.thisType, cls, parentRefs, paramDecls) - } - } - newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer) - } - - private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = - newSymbol(cls, name.encode, flags | Method, info).entered.asTerm - - private def newAliasType(name: TypeName, tpe: Type, flags: FlagSet = EmptyFlags): TypeSymbol = { - val sym = newSymbol(ScalaPackageClass, name, flags, TypeAlias(tpe)) - ScalaPackageClass.currentPackageDecls.enter(sym) - sym - } - - private def newPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, - resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { - val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) - val tparamBounds = tparamNames map (_ => TypeBounds.empty) - val ptype = PolyType(tparamNames)(_ => tparamBounds, resultTypeFn) - newMethod(cls, name, ptype, flags) - } - - private def newT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = - newPolyMethod(cls, name, 1, resultTypeFn, flags) - - private def newT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = - newPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags) - - private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[TypeRef] = { - val arr = new Array[TypeRef](arity + 1) - for (i <- countFrom to arity) arr(i) = ctx.requiredClassRef(name + i) - arr - } - - private def completeClass(cls: ClassSymbol): ClassSymbol = { - ensureConstructor(cls, EmptyScope) - if (cls.linkedClass.exists) cls.linkedClass.info = NoType - cls - } - - lazy val RootClass: ClassSymbol = ctx.newPackageSymbol( - NoSymbol, nme.ROOT, (root, rootcls) => ctx.rootLoader(root)).moduleClass.asClass - lazy val RootPackage: TermSymbol = ctx.newSymbol( - NoSymbol, nme.ROOTPKG, PackageCreationFlags, TypeRef(NoPrefix, RootClass)) - - lazy val EmptyPackageVal = ctx.newPackageSymbol( - RootClass, nme.EMPTY_PACKAGE, (emptypkg, emptycls) => ctx.rootLoader(emptypkg)).entered - lazy val EmptyPackageClass = EmptyPackageVal.moduleClass.asClass - - /** A package in which we can place all methods that are interpreted specially by the compiler */ - lazy val OpsPackageVal = ctx.newCompletePackageSymbol(RootClass, nme.OPS_PACKAGE).entered - lazy val OpsPackageClass = OpsPackageVal.moduleClass.asClass - - lazy val ScalaPackageVal = ctx.requiredPackage("scala") - lazy val ScalaMathPackageVal = ctx.requiredPackage("scala.math") - lazy val ScalaPackageClass = ScalaPackageVal.moduleClass.asClass - lazy val JavaPackageVal = ctx.requiredPackage("java") - lazy val JavaLangPackageVal = ctx.requiredPackage("java.lang") - // fundamental modules - lazy val SysPackage = ctx.requiredModule("scala.sys.package") - lazy val Sys_errorR = SysPackage.moduleClass.requiredMethodRef(nme.error) - def Sys_error(implicit ctx: Context) = Sys_errorR.symbol - - /** The `scalaShadowing` package is used to safely modify classes and - * objects in scala so that they can be used from dotty. They will - * be visible as members of the `scala` package, replacing any objects - * or classes with the same name. But their binary artifacts are - * in `scalaShadowing` so they don't clash with the same-named `scala` - * members at runtime. - */ - lazy val ScalaShadowingPackageVal = ctx.requiredPackage("scalaShadowing") - lazy val ScalaShadowingPackageClass = ScalaShadowingPackageVal.moduleClass.asClass - - /** Note: We cannot have same named methods defined in Object and Any (and AnyVal, for that matter) - * because after erasure the Any and AnyVal references get remapped to the Object methods - * which would result in a double binding assertion failure. - * Instead we do the following: - * - * - Have some methods exist only in Any, and remap them with the Erasure denotation - * transformer to be owned by Object. - * - Have other methods exist only in Object. - * To achieve this, we synthesize all Any and Object methods; Object methods no longer get - * loaded from a classfile. - * - * There's a remaining question about `getClass`. In Scala2.x `getClass` was handled by compiler magic. - * This is deemed too cumersome for Dotty and therefore right now `getClass` gets no special treatment; - * it's just a method on `Any` which returns the raw type `java.lang.Class`. An alternative - * way to get better `getClass` typing would be to treat `getClass` as a method of a generic - * decorator which gets remapped in a later phase to Object#getClass. Then we could give it - * the right type without changing the typechecker: - * - * implicit class AnyGetClass[T](val x: T) extends AnyVal { - * def getClass: java.lang.Class[T] = ??? - * } - */ - lazy val AnyClass: ClassSymbol = completeClass(newCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil)) - def AnyType = AnyClass.typeRef - lazy val AnyValClass: ClassSymbol = completeClass(newCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef))) - def AnyValType = AnyValClass.typeRef - - lazy val Any_== = newMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) - lazy val Any_!= = newMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) - lazy val Any_equals = newMethod(AnyClass, nme.equals_, methOfAny(BooleanType)) - lazy val Any_hashCode = newMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType)) - lazy val Any_toString = newMethod(AnyClass, nme.toString_, MethodType(Nil, StringType)) - lazy val Any_## = newMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) - lazy val Any_getClass = newMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) - lazy val Any_isInstanceOf = newT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) - lazy val Any_asInstanceOf = newT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final) - - def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode, - Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf) - - lazy val ObjectClass: ClassSymbol = { - val cls = ctx.requiredClass("java.lang.Object") - assert(!cls.isCompleted, "race for completing java.lang.Object") - cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) - - // The companion object doesn't really exist, `NoType` is the general - // technique to do that. Here we need to set it before completing - // attempt to load Object's classfile, which causes issue #1648. - val companion = JavaLangPackageVal.info.decl(nme.Object).symbol - companion.info = NoType // to indicate that it does not really exist - - completeClass(cls) - } - def ObjectType = ObjectClass.typeRef - - lazy val AnyRefAlias: TypeSymbol = newAliasType(tpnme.AnyRef, ObjectType) - def AnyRefType = AnyRefAlias.typeRef - - lazy val Object_eq = newMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) - lazy val Object_ne = newMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) - lazy val Object_synchronized = newPolyMethod(ObjectClass, nme.synchronized_, 1, - pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final) - lazy val Object_clone = newMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) - lazy val Object_finalize = newMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected) - lazy val Object_notify = newMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType)) - lazy val Object_notifyAll = newMethod(ObjectClass, nme.notifyAll_, MethodType(Nil, UnitType)) - lazy val Object_wait = newMethod(ObjectClass, nme.wait_, MethodType(Nil, UnitType)) - lazy val Object_waitL = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: Nil, UnitType)) - lazy val Object_waitLI = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: IntType :: Nil, UnitType)) - - def ObjectMethods = List(Object_eq, Object_ne, Object_synchronized, Object_clone, - Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI) - - /** Dummy method needed by elimByName */ - lazy val dummyApply = newPolyMethod( - OpsPackageClass, nme.dummyApply, 1, - pt => MethodType(List(FunctionOf(Nil, PolyParam(pt, 0))), PolyParam(pt, 0))) - - /** Method representing a throw */ - lazy val throwMethod = newMethod(OpsPackageClass, nme.THROWkw, - MethodType(List(ThrowableType), NothingType)) - - lazy val NothingClass: ClassSymbol = newCompleteClassSymbol( - ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyClass.typeRef)) - def NothingType = NothingClass.typeRef - lazy val NullClass: ClassSymbol = newCompleteClassSymbol( - ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef)) - def NullType = NullClass.typeRef - - lazy val ScalaPredefModuleRef = ctx.requiredModuleRef("scala.Predef") - def ScalaPredefModule(implicit ctx: Context) = ScalaPredefModuleRef.symbol - - lazy val Predef_conformsR = ScalaPredefModule.requiredMethodRef("$conforms") - def Predef_conforms(implicit ctx: Context) = Predef_conformsR.symbol - lazy val Predef_classOfR = ScalaPredefModule.requiredMethodRef("classOf") - def Predef_classOf(implicit ctx: Context) = Predef_classOfR.symbol - - lazy val ScalaRuntimeModuleRef = ctx.requiredModuleRef("scala.runtime.ScalaRunTime") - def ScalaRuntimeModule(implicit ctx: Context) = ScalaRuntimeModuleRef.symbol - def ScalaRuntimeClass(implicit ctx: Context) = ScalaRuntimeModule.moduleClass.asClass - - def runtimeMethodRef(name: PreName) = ScalaRuntimeModule.requiredMethodRef(name) - def ScalaRuntime_dropR(implicit ctx: Context) = runtimeMethodRef(nme.drop) - def ScalaRuntime_drop(implicit ctx: Context) = ScalaRuntime_dropR.symbol - - lazy val BoxesRunTimeModuleRef = ctx.requiredModuleRef("scala.runtime.BoxesRunTime") - def BoxesRunTimeModule(implicit ctx: Context) = BoxesRunTimeModuleRef.symbol - def BoxesRunTimeClass(implicit ctx: Context) = BoxesRunTimeModule.moduleClass.asClass - lazy val ScalaStaticsModuleRef = ctx.requiredModuleRef("scala.runtime.Statics") - def ScalaStaticsModule(implicit ctx: Context) = ScalaStaticsModuleRef.symbol - def ScalaStaticsClass(implicit ctx: Context) = ScalaStaticsModule.moduleClass.asClass - - def staticsMethodRef(name: PreName) = ScalaStaticsModule.requiredMethodRef(name) - def staticsMethod(name: PreName) = ScalaStaticsModule.requiredMethod(name) - - lazy val DottyPredefModuleRef = ctx.requiredModuleRef("dotty.DottyPredef") - def DottyPredefModule(implicit ctx: Context) = DottyPredefModuleRef.symbol - - def Predef_eqAny(implicit ctx: Context) = DottyPredefModule.requiredMethod(nme.eqAny) - - lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays") - def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol - def newGenericArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newGenericArray") - def newArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newArray") - - lazy val NilModuleRef = ctx.requiredModuleRef("scala.collection.immutable.Nil") - def NilModule(implicit ctx: Context) = NilModuleRef.symbol - - lazy val SingletonClass: ClassSymbol = - // needed as a synthetic class because Scala 2.x refers to it in classfiles - // but does not define it as an explicit class. - newCompleteClassSymbol( - ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final, - List(AnyClass.typeRef), EmptyScope) - - lazy val SeqType: TypeRef = ctx.requiredClassRef("scala.collection.Seq") - def SeqClass(implicit ctx: Context) = SeqType.symbol.asClass - - lazy val Seq_applyR = SeqClass.requiredMethodRef(nme.apply) - def Seq_apply(implicit ctx: Context) = Seq_applyR.symbol - lazy val Seq_headR = SeqClass.requiredMethodRef(nme.head) - def Seq_head(implicit ctx: Context) = Seq_headR.symbol - - lazy val ArrayType: TypeRef = ctx.requiredClassRef("scala.Array") - def ArrayClass(implicit ctx: Context) = ArrayType.symbol.asClass - lazy val Array_applyR = ArrayClass.requiredMethodRef(nme.apply) - def Array_apply(implicit ctx: Context) = Array_applyR.symbol - lazy val Array_updateR = ArrayClass.requiredMethodRef(nme.update) - def Array_update(implicit ctx: Context) = Array_updateR.symbol - lazy val Array_lengthR = ArrayClass.requiredMethodRef(nme.length) - def Array_length(implicit ctx: Context) = Array_lengthR.symbol - lazy val Array_cloneR = ArrayClass.requiredMethodRef(nme.clone_) - def Array_clone(implicit ctx: Context) = Array_cloneR.symbol - lazy val ArrayConstructorR = ArrayClass.requiredMethodRef(nme.CONSTRUCTOR) - def ArrayConstructor(implicit ctx: Context) = ArrayConstructorR.symbol - lazy val ArrayModuleType = ctx.requiredModuleRef("scala.Array") - def ArrayModule(implicit ctx: Context) = ArrayModuleType.symbol.moduleClass.asClass - - - lazy val UnitType: TypeRef = valueTypeRef("scala.Unit", BoxedUnitType, java.lang.Void.TYPE, UnitEnc) - def UnitClass(implicit ctx: Context) = UnitType.symbol.asClass - lazy val BooleanType = valueTypeRef("scala.Boolean", BoxedBooleanType, java.lang.Boolean.TYPE, BooleanEnc) - def BooleanClass(implicit ctx: Context) = BooleanType.symbol.asClass - lazy val Boolean_notR = BooleanClass.requiredMethodRef(nme.UNARY_!) - def Boolean_! = Boolean_notR.symbol - lazy val Boolean_andR = BooleanClass.requiredMethodRef(nme.ZAND) // ### harmonize required... calls - def Boolean_&& = Boolean_andR.symbol - lazy val Boolean_orR = BooleanClass.requiredMethodRef(nme.ZOR) - def Boolean_|| = Boolean_orR.symbol - - lazy val ByteType: TypeRef = valueTypeRef("scala.Byte", BoxedByteType, java.lang.Byte.TYPE, ByteEnc) - def ByteClass(implicit ctx: Context) = ByteType.symbol.asClass - lazy val ShortType: TypeRef = valueTypeRef("scala.Short", BoxedShortType, java.lang.Short.TYPE, ShortEnc) - def ShortClass(implicit ctx: Context) = ShortType.symbol.asClass - lazy val CharType: TypeRef = valueTypeRef("scala.Char", BoxedCharType, java.lang.Character.TYPE, CharEnc) - def CharClass(implicit ctx: Context) = CharType.symbol.asClass - lazy val IntType: TypeRef = valueTypeRef("scala.Int", BoxedIntType, java.lang.Integer.TYPE, IntEnc) - def IntClass(implicit ctx: Context) = IntType.symbol.asClass - lazy val Int_minusR = IntClass.requiredMethodRef(nme.MINUS, List(IntType)) - def Int_- = Int_minusR.symbol - lazy val Int_plusR = IntClass.requiredMethodRef(nme.PLUS, List(IntType)) - def Int_+ = Int_plusR.symbol - lazy val Int_divR = IntClass.requiredMethodRef(nme.DIV, List(IntType)) - def Int_/ = Int_divR.symbol - lazy val Int_mulR = IntClass.requiredMethodRef(nme.MUL, List(IntType)) - def Int_* = Int_mulR.symbol - lazy val Int_eqR = IntClass.requiredMethodRef(nme.EQ, List(IntType)) - def Int_== = Int_eqR.symbol - lazy val Int_geR = IntClass.requiredMethodRef(nme.GE, List(IntType)) - def Int_>= = Int_geR.symbol - lazy val Int_leR = IntClass.requiredMethodRef(nme.LE, List(IntType)) - def Int_<= = Int_leR.symbol - lazy val LongType: TypeRef = valueTypeRef("scala.Long", BoxedLongType, java.lang.Long.TYPE, LongEnc) - def LongClass(implicit ctx: Context) = LongType.symbol.asClass - lazy val Long_XOR_Long = LongType.member(nme.XOR).requiredSymbol( - x => (x is Method) && (x.info.firstParamTypes.head isRef defn.LongClass) - ) - lazy val Long_LSR_Int = LongType.member(nme.LSR).requiredSymbol( - x => (x is Method) && (x.info.firstParamTypes.head isRef defn.IntClass) - ) - lazy val FloatType: TypeRef = valueTypeRef("scala.Float", BoxedFloatType, java.lang.Float.TYPE, FloatEnc) - def FloatClass(implicit ctx: Context) = FloatType.symbol.asClass - lazy val DoubleType: TypeRef = valueTypeRef("scala.Double", BoxedDoubleType, java.lang.Double.TYPE, DoubleEnc) - def DoubleClass(implicit ctx: Context) = DoubleType.symbol.asClass - - lazy val BoxedUnitType: TypeRef = ctx.requiredClassRef("scala.runtime.BoxedUnit") - def BoxedUnitClass(implicit ctx: Context) = BoxedUnitType.symbol.asClass - - def BoxedUnit_UNIT(implicit ctx: Context) = BoxedUnitClass.linkedClass.requiredValue("UNIT") - - lazy val BoxedBooleanType: TypeRef = ctx.requiredClassRef("java.lang.Boolean") - def BoxedBooleanClass(implicit ctx: Context) = BoxedBooleanType.symbol.asClass - lazy val BoxedByteType: TypeRef = ctx.requiredClassRef("java.lang.Byte") - def BoxedByteClass(implicit ctx: Context) = BoxedByteType.symbol.asClass - lazy val BoxedShortType: TypeRef = ctx.requiredClassRef("java.lang.Short") - def BoxedShortClass(implicit ctx: Context) = BoxedShortType.symbol.asClass - lazy val BoxedCharType: TypeRef = ctx.requiredClassRef("java.lang.Character") - def BoxedCharClass(implicit ctx: Context) = BoxedCharType.symbol.asClass - lazy val BoxedIntType: TypeRef = ctx.requiredClassRef("java.lang.Integer") - def BoxedIntClass(implicit ctx: Context) = BoxedIntType.symbol.asClass - lazy val BoxedLongType: TypeRef = ctx.requiredClassRef("java.lang.Long") - def BoxedLongClass(implicit ctx: Context) = BoxedLongType.symbol.asClass - lazy val BoxedFloatType: TypeRef = ctx.requiredClassRef("java.lang.Float") - def BoxedFloatClass(implicit ctx: Context) = BoxedFloatType.symbol.asClass - lazy val BoxedDoubleType: TypeRef = ctx.requiredClassRef("java.lang.Double") - def BoxedDoubleClass(implicit ctx: Context) = BoxedDoubleType.symbol.asClass - - lazy val BoxedBooleanModule = ctx.requiredModule("java.lang.Boolean") - lazy val BoxedByteModule = ctx.requiredModule("java.lang.Byte") - lazy val BoxedShortModule = ctx.requiredModule("java.lang.Short") - lazy val BoxedCharModule = ctx.requiredModule("java.lang.Character") - lazy val BoxedIntModule = ctx.requiredModule("java.lang.Integer") - lazy val BoxedLongModule = ctx.requiredModule("java.lang.Long") - lazy val BoxedFloatModule = ctx.requiredModule("java.lang.Float") - lazy val BoxedDoubleModule = ctx.requiredModule("java.lang.Double") - lazy val BoxedUnitModule = ctx.requiredModule("java.lang.Void") - - lazy val ByNameParamClass2x = specialPolyClass(tpnme.BYNAME_PARAM_CLASS, Covariant, Seq(AnyType)) - lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, Seq(AnyType)) - - lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, Seq(ObjectType, SeqType)) - - // fundamental classes - lazy val StringClass = ctx.requiredClass("java.lang.String") - def StringType: Type = StringClass.typeRef - lazy val StringModule = StringClass.linkedClass - - lazy val String_+ = newMethod(StringClass, nme.raw.PLUS, methOfAny(StringType), Final) - lazy val String_valueOf_Object = StringModule.info.member(nme.valueOf).suchThat(_.info.firstParamTypes match { - case List(pt) => (pt isRef AnyClass) || (pt isRef ObjectClass) - case _ => false - }).symbol - - lazy val JavaCloneableClass = ctx.requiredClass("java.lang.Cloneable") - lazy val NullPointerExceptionClass = ctx.requiredClass("java.lang.NullPointerException") - lazy val ClassClass = ctx.requiredClass("java.lang.Class") - lazy val BoxedNumberClass = ctx.requiredClass("java.lang.Number") - lazy val ThrowableClass = ctx.requiredClass("java.lang.Throwable") - lazy val ClassCastExceptionClass = ctx.requiredClass("java.lang.ClassCastException") - lazy val JavaSerializableClass = ctx.requiredClass("java.lang.Serializable") - lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable") - - // in scalac modified to have Any as parent - - lazy val SerializableType: TypeRef = ctx.requiredClassRef("scala.Serializable") - def SerializableClass(implicit ctx: Context) = SerializableType.symbol.asClass - lazy val StringBuilderType: TypeRef = ctx.requiredClassRef("scala.collection.mutable.StringBuilder") - def StringBuilderClass(implicit ctx: Context) = StringBuilderType.symbol.asClass - lazy val MatchErrorType: TypeRef = ctx.requiredClassRef("scala.MatchError") - def MatchErrorClass(implicit ctx: Context) = MatchErrorType.symbol.asClass - - lazy val StringAddType: TypeRef = ctx.requiredClassRef("scala.runtime.StringAdd") - def StringAddClass(implicit ctx: Context) = StringAddType.symbol.asClass - - lazy val StringAdd_plusR = StringAddClass.requiredMethodRef(nme.raw.PLUS) - def StringAdd_+(implicit ctx: Context) = StringAdd_plusR.symbol - - lazy val PartialFunctionType: TypeRef = ctx.requiredClassRef("scala.PartialFunction") - def PartialFunctionClass(implicit ctx: Context) = PartialFunctionType.symbol.asClass - lazy val AbstractPartialFunctionType: TypeRef = ctx.requiredClassRef("scala.runtime.AbstractPartialFunction") - def AbstractPartialFunctionClass(implicit ctx: Context) = AbstractPartialFunctionType.symbol.asClass - lazy val SymbolType: TypeRef = ctx.requiredClassRef("scala.Symbol") - def SymbolClass(implicit ctx: Context) = SymbolType.symbol.asClass - lazy val DynamicType: TypeRef = ctx.requiredClassRef("scala.Dynamic") - def DynamicClass(implicit ctx: Context) = DynamicType.symbol.asClass - lazy val OptionType: TypeRef = ctx.requiredClassRef("scala.Option") - def OptionClass(implicit ctx: Context) = OptionType.symbol.asClass - lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product") - def ProductClass(implicit ctx: Context) = ProductType.symbol.asClass - lazy val Product_canEqualR = ProductClass.requiredMethodRef(nme.canEqual_) - def Product_canEqual(implicit ctx: Context) = Product_canEqualR.symbol - lazy val Product_productArityR = ProductClass.requiredMethodRef(nme.productArity) - def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol - lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix) - def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol - lazy val LanguageModuleRef = ctx.requiredModule("scala.language") - def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass - lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl") - - lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag") - def ClassTagClass(implicit ctx: Context) = ClassTagType.symbol.asClass - def ClassTagModule(implicit ctx: Context) = ClassTagClass.companionModule - - lazy val EqType = ctx.requiredClassRef("scala.Eq") - def EqClass(implicit ctx: Context) = EqType.symbol.asClass - - // Annotation base classes - lazy val AnnotationType = ctx.requiredClassRef("scala.annotation.Annotation") - def AnnotationClass(implicit ctx: Context) = AnnotationType.symbol.asClass - lazy val ClassfileAnnotationType = ctx.requiredClassRef("scala.annotation.ClassfileAnnotation") - def ClassfileAnnotationClass(implicit ctx: Context) = ClassfileAnnotationType.symbol.asClass - lazy val StaticAnnotationType = ctx.requiredClassRef("scala.annotation.StaticAnnotation") - def StaticAnnotationClass(implicit ctx: Context) = StaticAnnotationType.symbol.asClass - - // Annotation classes - lazy val AliasAnnotType = ctx.requiredClassRef("scala.annotation.internal.Alias") - def AliasAnnot(implicit ctx: Context) = AliasAnnotType.symbol.asClass - lazy val AnnotationDefaultAnnotType = ctx.requiredClassRef("scala.annotation.internal.AnnotationDefault") - def AnnotationDefaultAnnot(implicit ctx: Context) = AnnotationDefaultAnnotType.symbol.asClass - lazy val BodyAnnotType = ctx.requiredClassRef("scala.annotation.internal.Body") - def BodyAnnot(implicit ctx: Context) = BodyAnnotType.symbol.asClass - lazy val ChildAnnotType = ctx.requiredClassRef("scala.annotation.internal.Child") - def ChildAnnot(implicit ctx: Context) = ChildAnnotType.symbol.asClass - lazy val CovariantBetweenAnnotType = ctx.requiredClassRef("scala.annotation.internal.CovariantBetween") - def CovariantBetweenAnnot(implicit ctx: Context) = CovariantBetweenAnnotType.symbol.asClass - lazy val ContravariantBetweenAnnotType = ctx.requiredClassRef("scala.annotation.internal.ContravariantBetween") - def ContravariantBetweenAnnot(implicit ctx: Context) = ContravariantBetweenAnnotType.symbol.asClass - lazy val DeprecatedAnnotType = ctx.requiredClassRef("scala.deprecated") - def DeprecatedAnnot(implicit ctx: Context) = DeprecatedAnnotType.symbol.asClass - lazy val ImplicitNotFoundAnnotType = ctx.requiredClassRef("scala.annotation.implicitNotFound") - def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass - lazy val InlineAnnotType = ctx.requiredClassRef("scala.inline") - def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass - lazy val InlineParamAnnotType = ctx.requiredClassRef("scala.annotation.internal.InlineParam") - def InlineParamAnnot(implicit ctx: Context) = InlineParamAnnotType.symbol.asClass - lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("scala.annotation.internal.InvariantBetween") - def InvariantBetweenAnnot(implicit ctx: Context) = InvariantBetweenAnnotType.symbol.asClass - lazy val MigrationAnnotType = ctx.requiredClassRef("scala.annotation.migration") - def MigrationAnnot(implicit ctx: Context) = MigrationAnnotType.symbol.asClass - lazy val NativeAnnotType = ctx.requiredClassRef("scala.native") - def NativeAnnot(implicit ctx: Context) = NativeAnnotType.symbol.asClass - lazy val RemoteAnnotType = ctx.requiredClassRef("scala.remote") - def RemoteAnnot(implicit ctx: Context) = RemoteAnnotType.symbol.asClass - lazy val RepeatedAnnotType = ctx.requiredClassRef("scala.annotation.internal.Repeated") - def RepeatedAnnot(implicit ctx: Context) = RepeatedAnnotType.symbol.asClass - lazy val SourceFileAnnotType = ctx.requiredClassRef("scala.annotation.internal.SourceFile") - def SourceFileAnnot(implicit ctx: Context) = SourceFileAnnotType.symbol.asClass - lazy val ScalaSignatureAnnotType = ctx.requiredClassRef("scala.reflect.ScalaSignature") - def ScalaSignatureAnnot(implicit ctx: Context) = ScalaSignatureAnnotType.symbol.asClass - lazy val ScalaLongSignatureAnnotType = ctx.requiredClassRef("scala.reflect.ScalaLongSignature") - def ScalaLongSignatureAnnot(implicit ctx: Context) = ScalaLongSignatureAnnotType.symbol.asClass - lazy val ScalaStrictFPAnnotType = ctx.requiredClassRef("scala.annotation.strictfp") - def ScalaStrictFPAnnot(implicit ctx: Context) = ScalaStrictFPAnnotType.symbol.asClass - lazy val ScalaStaticAnnotType = ctx.requiredClassRef("scala.annotation.static") - def ScalaStaticAnnot(implicit ctx: Context) = ScalaStaticAnnotType.symbol.asClass - lazy val SerialVersionUIDAnnotType = ctx.requiredClassRef("scala.SerialVersionUID") - def SerialVersionUIDAnnot(implicit ctx: Context) = SerialVersionUIDAnnotType.symbol.asClass - lazy val TASTYSignatureAnnotType = ctx.requiredClassRef("scala.annotation.internal.TASTYSignature") - def TASTYSignatureAnnot(implicit ctx: Context) = TASTYSignatureAnnotType.symbol.asClass - lazy val TASTYLongSignatureAnnotType = ctx.requiredClassRef("scala.annotation.internal.TASTYLongSignature") - def TASTYLongSignatureAnnot(implicit ctx: Context) = TASTYLongSignatureAnnotType.symbol.asClass - lazy val TailrecAnnotType = ctx.requiredClassRef("scala.annotation.tailrec") - def TailrecAnnot(implicit ctx: Context) = TailrecAnnotType.symbol.asClass - lazy val SwitchAnnotType = ctx.requiredClassRef("scala.annotation.switch") - def SwitchAnnot(implicit ctx: Context) = SwitchAnnotType.symbol.asClass - lazy val ThrowsAnnotType = ctx.requiredClassRef("scala.throws") - def ThrowsAnnot(implicit ctx: Context) = ThrowsAnnotType.symbol.asClass - lazy val TransientAnnotType = ctx.requiredClassRef("scala.transient") - def TransientAnnot(implicit ctx: Context) = TransientAnnotType.symbol.asClass - lazy val UncheckedAnnotType = ctx.requiredClassRef("scala.unchecked") - def UncheckedAnnot(implicit ctx: Context) = UncheckedAnnotType.symbol.asClass - lazy val UncheckedStableAnnotType = ctx.requiredClassRef("scala.annotation.unchecked.uncheckedStable") - def UncheckedStableAnnot(implicit ctx: Context) = UncheckedStableAnnotType.symbol.asClass - lazy val UncheckedVarianceAnnotType = ctx.requiredClassRef("scala.annotation.unchecked.uncheckedVariance") - def UncheckedVarianceAnnot(implicit ctx: Context) = UncheckedVarianceAnnotType.symbol.asClass - lazy val UnsafeNonvariantAnnotType = ctx.requiredClassRef("scala.annotation.internal.UnsafeNonvariant") - def UnsafeNonvariantAnnot(implicit ctx: Context) = UnsafeNonvariantAnnotType.symbol.asClass - lazy val VolatileAnnotType = ctx.requiredClassRef("scala.volatile") - def VolatileAnnot(implicit ctx: Context) = VolatileAnnotType.symbol.asClass - lazy val FieldMetaAnnotType = ctx.requiredClassRef("scala.annotation.meta.field") - def FieldMetaAnnot(implicit ctx: Context) = FieldMetaAnnotType.symbol.asClass - lazy val GetterMetaAnnotType = ctx.requiredClassRef("scala.annotation.meta.getter") - def GetterMetaAnnot(implicit ctx: Context) = GetterMetaAnnotType.symbol.asClass - lazy val SetterMetaAnnotType = ctx.requiredClassRef("scala.annotation.meta.setter") - def SetterMetaAnnot(implicit ctx: Context) = SetterMetaAnnotType.symbol.asClass - - // convenient one-parameter method types - def methOfAny(tp: Type) = MethodType(List(AnyType), tp) - def methOfAnyVal(tp: Type) = MethodType(List(AnyValType), tp) - def methOfAnyRef(tp: Type) = MethodType(List(ObjectType), tp) - - // Derived types - - def RepeatedParamType = RepeatedParamClass.typeRef - def ThrowableType = ThrowableClass.typeRef - - def ClassType(arg: Type)(implicit ctx: Context) = { - val ctype = ClassClass.typeRef - if (ctx.phase.erasedTypes) ctype else ctype.appliedTo(arg) - } - - /** The enumeration type, goven a value of the enumeration */ - def EnumType(sym: Symbol)(implicit ctx: Context) = - // given (in java): "class A { enum E { VAL1 } }" - // - sym: the symbol of the actual enumeration value (VAL1) - // - .owner: the ModuleClassSymbol of the enumeration (object E) - // - .linkedClass: the ClassSymbol of the enumeration (class E) - sym.owner.linkedClass.typeRef - - object FunctionOf { - def apply(args: List[Type], resultType: Type)(implicit ctx: Context) = - FunctionType(args.length).appliedTo(args ::: resultType :: Nil) - def unapply(ft: Type)(implicit ctx: Context)/*: Option[(List[Type], Type)]*/ = { - // -language:keepUnions difference: unapply needs result type because inferred type - // is Some[(List[Type], Type)] | None, which is not a legal unapply type. - val tsym = ft.typeSymbol - lazy val targs = ft.argInfos - val numArgs = targs.length - 1 - if (numArgs >= 0 && numArgs <= MaxFunctionArity && - (FunctionType(numArgs).symbol == tsym)) Some(targs.init, targs.last) - else None - } - } - - object ArrayOf { - def apply(elem: Type)(implicit ctx: Context) = - if (ctx.erasedTypes) JavaArrayType(elem) - else ArrayType.appliedTo(elem :: Nil) - def unapply(tp: Type)(implicit ctx: Context): Option[Type] = tp.dealias match { - case at: RefinedType if (at isRef ArrayType.symbol) && at.argInfos.length == 1 => Some(at.argInfos.head) - case _ => None - } - } - - /** An extractor for multi-dimensional arrays. - * Note that this will also extract the high bound if an - * element type is a wildcard. E.g. - * - * Array[_ <: Array[_ <: Number]] - * - * would match - * - * MultiArrayOf(<Number>, 2) - */ - object MultiArrayOf { - def apply(elem: Type, ndims: Int)(implicit ctx: Context): Type = - if (ndims == 0) elem else ArrayOf(apply(elem, ndims - 1)) - def unapply(tp: Type)(implicit ctx: Context): Option[(Type, Int)] = tp match { - case ArrayOf(elemtp) => - def recur(elemtp: Type): Option[(Type, Int)] = elemtp.dealias match { - case TypeBounds(lo, hi) => recur(hi) - case MultiArrayOf(finalElemTp, n) => Some(finalElemTp, n + 1) - case _ => Some(elemtp, 1) - } - recur(elemtp) - case _ => - None - } - } - - // ----- Symbol sets --------------------------------------------------- - - lazy val AbstractFunctionType = mkArityArray("scala.runtime.AbstractFunction", MaxAbstractFunctionArity, 0) - val AbstractFunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => AbstractFunctionType.map(_.symbol.asClass)) - def AbstractFunctionClass(n: Int)(implicit ctx: Context) = AbstractFunctionClassPerRun()(ctx)(n) - lazy val FunctionType = mkArityArray("scala.Function", MaxFunctionArity, 0) - def FunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => FunctionType.map(_.symbol.asClass)) - def FunctionClass(n: Int)(implicit ctx: Context) = FunctionClassPerRun()(ctx)(n) - lazy val Function0_applyR = FunctionType(0).symbol.requiredMethodRef(nme.apply) - def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol - - lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2) - lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0) - - private lazy val FunctionTypes: Set[TypeRef] = FunctionType.toSet - private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet - private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet - - /** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */ - def scalaClassName(cls: Symbol)(implicit ctx: Context): TypeName = - if (cls.isClass && cls.owner == ScalaPackageClass) cls.asClass.name else EmptyTypeName - - /** If type `ref` refers to a class in the scala package, its name, otherwise EmptyTypeName */ - def scalaClassName(ref: Type)(implicit ctx: Context): TypeName = scalaClassName(ref.classSymbol) - - private def isVarArityClass(cls: Symbol, prefix: Name) = { - val name = scalaClassName(cls) - name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit) - } - - def isBottomClass(cls: Symbol) = - cls == NothingClass || cls == NullClass - def isBottomType(tp: Type) = - tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) - - def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function) - def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction) - def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple) - def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product) - - val StaticRootImportFns = List[() => TermRef]( - () => JavaLangPackageVal.termRef, - () => ScalaPackageVal.termRef - ) - - val PredefImportFns = List[() => TermRef]( - () => ScalaPredefModuleRef, - () => DottyPredefModuleRef - ) - - lazy val RootImportFns = - if (ctx.settings.YnoImports.value) List.empty[() => TermRef] - else if (ctx.settings.YnoPredef.value) StaticRootImportFns - else StaticRootImportFns ++ PredefImportFns - - lazy val RootImportTypes = RootImportFns.map(_()) - - /** Modules whose members are in the default namespace and their module classes */ - lazy val UnqualifiedOwnerTypes: Set[NamedType] = - RootImportTypes.toSet[NamedType] ++ RootImportTypes.map(_.symbol.moduleClass.typeRef) - - lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass) - - def isPolymorphicAfterErasure(sym: Symbol) = - (sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) - - def isTupleType(tp: Type)(implicit ctx: Context) = { - val arity = tp.dealias.argInfos.length - arity <= MaxTupleArity && TupleType(arity) != null && (tp isRef TupleType(arity).symbol) - } - - def tupleType(elems: List[Type]) = { - TupleType(elems.size).appliedTo(elems) - } - - def isProductSubType(tp: Type)(implicit ctx: Context) = - (tp derivesFrom ProductType.symbol) && tp.baseClasses.exists(isProductClass) - - def isFunctionType(tp: Type)(implicit ctx: Context) = { - val arity = functionArity(tp) - 0 <= arity && arity <= MaxFunctionArity && (tp isRef FunctionType(arity).symbol) - } - - def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 - - // ----- primitive value class machinery ------------------------------------------ - - /** This class would also be obviated by the implicit function type design */ - class PerRun[T](generate: Context => T) { - private var current: RunId = NoRunId - private var cached: T = _ - def apply()(implicit ctx: Context): T = { - if (current != ctx.runId) { - cached = generate(ctx) - current = ctx.runId - } - cached - } - } - - lazy val ScalaNumericValueTypeList = List( - ByteType, ShortType, CharType, IntType, LongType, FloatType, DoubleType) - - private lazy val ScalaNumericValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypeList.toSet - private lazy val ScalaValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypes + UnitType + BooleanType - private lazy val ScalaBoxedTypes = ScalaValueTypes map (t => boxedTypes(t.name)) - - val ScalaNumericValueClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaNumericValueTypes.map(_.symbol)) - val ScalaValueClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaValueTypes.map(_.symbol)) - val ScalaBoxedClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaBoxedTypes.map(_.symbol)) - - private val boxedTypes = mutable.Map[TypeName, TypeRef]() - private val valueTypeEnc = mutable.Map[TypeName, PrimitiveClassEnc]() - -// private val unboxedTypeRef = mutable.Map[TypeName, TypeRef]() -// private val javaTypeToValueTypeRef = mutable.Map[Class[_], TypeRef]() -// private val valueTypeNameToJavaType = mutable.Map[TypeName, Class[_]]() - - private def valueTypeRef(name: String, boxed: TypeRef, jtype: Class[_], enc: Int): TypeRef = { - val vcls = ctx.requiredClassRef(name) - boxedTypes(vcls.name) = boxed - valueTypeEnc(vcls.name) = enc -// unboxedTypeRef(boxed.name) = vcls -// javaTypeToValueTypeRef(jtype) = vcls -// valueTypeNameToJavaType(vcls.name) = jtype - vcls - } - - /** The type of the boxed class corresponding to primitive value type `tp`. */ - def boxedType(tp: Type)(implicit ctx: Context): TypeRef = boxedTypes(scalaClassName(tp)) - - def wrapArrayMethodName(elemtp: Type): TermName = { - val cls = elemtp.classSymbol - if (cls.isPrimitiveValueClass) nme.wrapXArray(cls.name) - else if (cls.derivesFrom(ObjectClass) && !cls.isPhantomClass) nme.wrapRefArray - else nme.genericWrapArray - } - - type PrimitiveClassEnc = Int - - val ByteEnc = 2 - val ShortEnc = ByteEnc * 3 - val CharEnc = 5 - val IntEnc = ShortEnc * CharEnc - val LongEnc = IntEnc * 7 - val FloatEnc = LongEnc * 11 - val DoubleEnc = FloatEnc * 13 - val BooleanEnc = 17 - val UnitEnc = 19 - - def isValueSubType(tref1: TypeRef, tref2: TypeRef)(implicit ctx: Context) = - valueTypeEnc(tref2.name) % valueTypeEnc(tref1.name) == 0 - def isValueSubClass(sym1: Symbol, sym2: Symbol) = - valueTypeEnc(sym2.asClass.name) % valueTypeEnc(sym1.asClass.name) == 0 - - // ----- Initialization --------------------------------------------------- - - /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ - lazy val syntheticScalaClasses = List( - AnyClass, - AnyRefAlias, - RepeatedParamClass, - ByNameParamClass2x, - AnyValClass, - NullClass, - NothingClass, - SingletonClass, - EqualsPatternClass) - - lazy val syntheticCoreClasses = syntheticScalaClasses ++ List( - EmptyPackageVal, - OpsPackageClass) - - /** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ - lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod) - - lazy val reservedScalaClassNames: Set[Name] = syntheticScalaClasses.map(_.name).toSet - - private[this] var _isInitialized = false - private def isInitialized = _isInitialized - - def init()(implicit ctx: Context) = { - this.ctx = ctx - if (!_isInitialized) { - // force initialization of every symbol that is synthesized or hijacked by the compiler - val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() - - // Enter all symbols from the scalaShadowing package in the scala package - for (m <- ScalaShadowingPackageClass.info.decls) - ScalaPackageClass.enter(m) - - _isInitialized = true - } - } -} diff --git a/src/dotty/tools/dotc/core/DenotTransformers.scala b/src/dotty/tools/dotc/core/DenotTransformers.scala deleted file mode 100644 index 02d27ea33..000000000 --- a/src/dotty/tools/dotc/core/DenotTransformers.scala +++ /dev/null @@ -1,78 +0,0 @@ -package dotty.tools.dotc -package core - -import Periods._ -import SymDenotations._ -import Contexts._ -import Types._ -import Symbols._ -import Denotations._ -import Phases._ -import java.lang.AssertionError -import dotty.tools.dotc.util.DotClass - -object DenotTransformers { - - /** A transformer group contains a sequence of transformers, - * ordered by the phase where they apply. Transformers are added - * to a group via `install`. - */ - - /** A transformer transforms denotations at a given phase */ - trait DenotTransformer extends Phase { - - /** The last phase during which the transformed denotations are valid */ - def lastPhaseId(implicit ctx: Context) = ctx.nextDenotTransformerId(id + 1) - - /** The validity period of the transformer in the given context */ - def validFor(implicit ctx: Context): Period = - Period(ctx.runId, id, lastPhaseId) - - /** The transformation method */ - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation - } - - /** A transformer that only transforms the info field of denotations */ - trait InfoTransformer extends DenotTransformer { - - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type - - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = { - val sym = ref.symbol - if (sym.exists && !mayChange(sym)) ref - else { - val info1 = transformInfo(ref.info, ref.symbol) - if (info1 eq ref.info) ref - else ref match { - case ref: SymDenotation => ref.copySymDenotation(info = info1) - case _ => ref.derivedSingleDenotation(ref.symbol, info1) - } - } - } - - /** Denotations with a symbol where `mayChange` is false are guaranteed to be - * unaffected by this transform, so `transformInfo` need not be run. This - * can save time, and more importantly, can help avoid forcing symbol completers. - */ - protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = true - } - - /** A transformer that only transforms SymDenotations */ - trait SymTransformer extends DenotTransformer { - - def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation - - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { - case ref: SymDenotation => transformSym(ref) - case _ => ref - } - } - - /** A `DenotTransformer` trait that has the identity as its `transform` method. - * You might want to inherit from this trait so that new denotations can be - * installed using `installAfter` and `enteredAfter` at the end of the phase. - */ - trait IdentityDenotTransformer extends DenotTransformer { - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref - } -} diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala deleted file mode 100644 index 6a39c5787..000000000 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ /dev/null @@ -1,1217 +0,0 @@ -package dotty.tools -package dotc -package core - -import SymDenotations.{ SymDenotation, ClassDenotation, NoDenotation } -import Contexts.{Context, ContextBase} -import Names.{Name, PreName} -import Names.TypeName -import StdNames._ -import Symbols.NoSymbol -import Symbols._ -import Types._ -import Periods._ -import Flags._ -import DenotTransformers._ -import Decorators._ -import dotc.transform.Erasure -import printing.Texts._ -import printing.Printer -import io.AbstractFile -import config.Config -import util.common._ -import collection.mutable.ListBuffer -import Decorators.SymbolIteratorDecorator - -/** Denotations represent the meaning of symbols and named types. - * The following diagram shows how the principal types of denotations - * and their denoting entities relate to each other. Lines ending in - * a down-arrow `v` are member methods. The two methods shown in the diagram are - * "symbol" and "deref". Both methods are parameterized by the current context, - * and are effectively indexed by current period. - * - * Lines ending in a horizontal line mean subtying (right is a subtype of left). - * - * NamedType------TermRefWithSignature - * | | Symbol---------ClassSymbol - * | | | | - * | denot | denot | denot | denot - * v v v v - * Denotation-+-----SingleDenotation-+------SymDenotation-+----ClassDenotation - * | | - * +-----MultiDenotation | - * | - * +--UniqueRefDenotation - * +--JointRefDenotation - * - * Here's a short summary of the classes in this diagram. - * - * NamedType A type consisting of a prefix type and a name, with fields - * prefix: Type - * name: Name - * It has two subtypes: TermRef and TypeRef - * TermRefWithSignature A TermRef that has in addition a signature to select an overloaded variant, with new field - * sig: Signature - * Symbol A label for a definition or declaration in one compiler run - * ClassSymbol A symbol representing a class - * Denotation The meaning of a named type or symbol during a period - * MultiDenotation A denotation representing several overloaded members - * SingleDenotation A denotation representing a non-overloaded member or definition, with main fields - * symbol: Symbol - * info: Type - * UniqueRefDenotation A denotation referring to a single definition with some member type - * JointRefDenotation A denotation referring to a member that could resolve to several definitions - * SymDenotation A denotation representing a single definition with its original type, with main fields - * name: Name - * owner: Symbol - * flags: Flags - * privateWithin: Symbol - * annotations: List[Annotation] - * ClassDenotation A denotation representing a single class definition. - */ -object Denotations { - - implicit def eqDenotation: Eq[Denotation, Denotation] = Eq - - /** A denotation is the result of resolving - * a name (either simple identifier or select) during a given period. - * - * Denotations can be combined with `&` and `|`. - * & is conjunction, | is disjunction. - * - * `&` will create an overloaded denotation from two - * non-overloaded denotations if their signatures differ. - * Analogously `|` of two denotations with different signatures will give - * an empty denotation `NoDenotation`. - * - * A denotation might refer to `NoSymbol`. This is the case if the denotation - * was produced from a disjunction of two denotations with different symbols - * and there was no common symbol in a superclass that could substitute for - * both symbols. Here is an example: - * - * Say, we have: - * - * class A { def f: A } - * class B { def f: B } - * val x: A | B = if (test) new A else new B - * val y = x.f - * - * Then the denotation of `y` is `SingleDenotation(NoSymbol, A | B)`. - * - * @param symbol The referencing symbol, or NoSymbol is none exists - */ - abstract class Denotation(val symbol: Symbol) extends util.DotClass with printing.Showable { - - /** The type info of the denotation, exists only for non-overloaded denotations */ - def info(implicit ctx: Context): Type - - /** The type info, or, if this is a SymDenotation where the symbol - * is not yet completed, the completer - */ - def infoOrCompleter: Type - - /** The period during which this denotation is valid. */ - def validFor: Period - - /** Is this a reference to a type symbol? */ - def isType: Boolean - - /** Is this a reference to a term symbol? */ - def isTerm: Boolean = !isType - - /** Is this denotation overloaded? */ - final def isOverloaded = isInstanceOf[MultiDenotation] - - /** The signature of the denotation. */ - def signature(implicit ctx: Context): Signature - - /** Resolve overloaded denotation to pick the ones with the given signature - * when seen from prefix `site`. - * @param relaxed When true, consider only parameter signatures for a match. - */ - def atSignature(sig: Signature, site: Type = NoPrefix, relaxed: Boolean = false)(implicit ctx: Context): Denotation - - /** The variant of this denotation that's current in the given context. - * If no such denotation exists, returns the denotation with each alternative - * at its first point of definition. - */ - def current(implicit ctx: Context): Denotation - - /** Is this denotation different from NoDenotation or an ErrorDenotation? */ - def exists: Boolean = true - - /** A denotation with the info of this denotation transformed using `f` */ - def mapInfo(f: Type => Type)(implicit ctx: Context): Denotation - - /** If this denotation does not exist, fallback to alternative */ - final def orElse(that: => Denotation) = if (this.exists) this else that - - /** The set of alternative single-denotations making up this denotation */ - final def alternatives: List[SingleDenotation] = altsWith(alwaysTrue) - - /** The alternatives of this denotation that satisfy the predicate `p`. */ - def altsWith(p: Symbol => Boolean): List[SingleDenotation] - - /** The unique alternative of this denotation that satisfies the predicate `p`, - * or NoDenotation if no satisfying alternative exists. - * @throws TypeError if there is at more than one alternative that satisfies `p`. - */ - def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation - - /** If this is a SingleDenotation, return it, otherwise throw a TypeError */ - def checkUnique(implicit ctx: Context): SingleDenotation = suchThat(alwaysTrue) - - /** Does this denotation have an alternative that satisfies the predicate `p`? */ - def hasAltWith(p: SingleDenotation => Boolean): Boolean - - /** The denotation made up from the alternatives of this denotation that - * are accessible from prefix `pre`, or NoDenotation if no accessible alternative exists. - */ - def accessibleFrom(pre: Type, superAccess: Boolean = false)(implicit ctx: Context): Denotation - - /** Find member of this denotation with given name and - * produce a denotation that contains the type of the member - * as seen from given prefix `pre`. Exclude all members that have - * flags in `excluded` from consideration. - */ - def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = - info.findMember(name, pre, excluded) - - /** If this denotation is overloaded, filter with given predicate. - * If result is still overloaded throw a TypeError. - * Note: disambiguate is slightly different from suchThat in that - * single-denotations that do not satisfy the predicate are left alone - * (whereas suchThat would map them to NoDenotation). - */ - def disambiguate(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation = this match { - case sdenot: SingleDenotation => sdenot - case mdenot => suchThat(p) orElse NoQualifyingRef(alternatives) - } - - /** Return symbol in this denotation that satisfies the given predicate. - * if generateStubs is specified, return a stubsymbol if denotation is a missing ref. - * Throw a `TypeError` if predicate fails to disambiguate symbol or no alternative matches. - */ - def requiredSymbol(p: Symbol => Boolean, source: AbstractFile = null, generateStubs: Boolean = true)(implicit ctx: Context): Symbol = - disambiguate(p) match { - case m @ MissingRef(ownerd, name) => - if (generateStubs) { - m.ex.printStackTrace() - ctx.newStubSymbol(ownerd.symbol, name, source) - } - else NoSymbol - case NoDenotation | _: NoQualifyingRef => - throw new TypeError(s"None of the alternatives of $this satisfies required predicate") - case denot => - denot.symbol - } - - def requiredMethod(name: PreName)(implicit ctx: Context): TermSymbol = - info.member(name.toTermName).requiredSymbol(_ is Method).asTerm - def requiredMethodRef(name: PreName)(implicit ctx: Context): TermRef = - requiredMethod(name).termRef - - def requiredMethod(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol = - info.member(name.toTermName).requiredSymbol(x=> - (x is Method) && x.info.paramTypess == List(argTypes) - ).asTerm - def requiredMethodRef(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermRef = - requiredMethod(name, argTypes).termRef - - def requiredValue(name: PreName)(implicit ctx: Context): TermSymbol = - info.member(name.toTermName).requiredSymbol(_.info.isParameterless).asTerm - def requiredValueRef(name: PreName)(implicit ctx: Context): TermRef = - requiredValue(name).termRef - - def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol = - info.member(name.toTypeName).requiredSymbol(_.isClass).asClass - - /** The alternative of this denotation that has a type matching `targetType` when seen - * as a member of type `site`, `NoDenotation` if none exists. - */ - def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation = { - def qualifies(sym: Symbol) = site.memberInfo(sym).matchesLoosely(targetType) - if (isOverloaded) { - atSignature(targetType.signature, site, relaxed = true) match { - case sd: SingleDenotation => sd.matchingDenotation(site, targetType) - case md => md.suchThat(qualifies(_)) - } - } - else if (exists && !qualifies(symbol)) NoDenotation - else asSingleDenotation - } - - /** Handle merge conflict by throwing a `MergeError` exception */ - private def mergeConflict(tp1: Type, tp2: Type)(implicit ctx: Context): Type = { - def showType(tp: Type) = tp match { - case ClassInfo(_, cls, _, _, _) => cls.showLocated - case bounds: TypeBounds => i"type bounds $bounds" - case _ => tp.show - } - if (true) throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}", tp1, tp2) - else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging - } - - /** Merge two lists of names. If names in corresponding positions match, keep them, - * otherwise generate new synthetic names. - */ - def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = { - for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped) - yield if (name1 == name2) name1 else syntheticName(idx) - }.toList - - /** Form a denotation by conjoining with denotation `that`. - * - * NoDenotations are dropped. MultiDenotations are handled by merging - * parts with same signatures. SingleDenotations with equal signatures - * are joined as follows: - * - * In a first step, consider only those denotations which have symbols - * that are accessible from prefix `pre`. - * - * If there are several such denotations, try to pick one by applying the following - * three precedence rules in decreasing order of priority: - * - * 1. Prefer denotations with more specific infos. - * 2. If infos are equally specific, prefer denotations with concrete symbols over denotations - * with abstract symbols. - * 3. If infos are equally specific and symbols are equally concrete, - * prefer denotations with symbols defined in subclasses - * over denotations with symbols defined in proper superclasses. - * - * If there is exactly one (preferred) accessible denotation, return it. - * - * If there is no preferred accessible denotation, return a JointRefDenotation - * with one of the operand symbols (unspecified which one), and an info which - * is the intersection (using `&` or `safe_&` if `safeIntersection` is true) - * of the infos of the operand denotations. - * - * If SingleDenotations with different signatures are joined, return NoDenotation. - */ - def & (that: Denotation, pre: Type, safeIntersection: Boolean = false)(implicit ctx: Context): Denotation = { - - /** Normally, `tp1 & tp2`. Special cases for matching methods and classes, with - * the possibility of raising a merge error. - */ - def infoMeet(tp1: Type, tp2: Type): Type = { - if (tp1 eq tp2) tp1 - else tp1 match { - case tp1: TypeBounds => - tp2 match { - case tp2: TypeBounds => if (safeIntersection) tp1 safe_& tp2 else tp1 & tp2 - case tp2: ClassInfo if tp1 contains tp2 => tp2 - case _ => mergeConflict(tp1, tp2) - } - case tp1: ClassInfo => - tp2 match { - case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix & tp2.prefix) - case tp2: TypeBounds if tp2 contains tp1 => tp1 - case _ => mergeConflict(tp1, tp2) - } - case tp1 @ MethodType(names1, formals1) if isTerm => - tp2 match { - case tp2 @ MethodType(names2, formals2) if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedMethodType( - mergeNames(names1, names2, nme.syntheticParamName), - formals1, - infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) - case _ => - mergeConflict(tp1, tp2) - } - case tp1: PolyType if isTerm => - tp2 match { - case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) => - tp1.derivedPolyType( - mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), - tp1.paramBounds, - infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) - case _: MethodicType => - mergeConflict(tp1, tp2) - } - case _ => - tp1 & tp2 - } - } - - /** Try to merge denot1 and denot2 without adding a new signature. */ - def mergeDenot(denot1: Denotation, denot2: SingleDenotation): Denotation = denot1 match { - case denot1 @ MultiDenotation(denot11, denot12) => - val d1 = mergeDenot(denot11, denot2) - if (d1.exists) denot1.derivedMultiDenotation(d1, denot12) - else { - val d2 = mergeDenot(denot12, denot2) - if (d2.exists) denot1.derivedMultiDenotation(denot11, d2) - else NoDenotation - } - case denot1: SingleDenotation => - if (denot1 eq denot2) denot1 - else if (denot1.matches(denot2)) mergeSingleDenot(denot1, denot2) - else NoDenotation - } - - /** Try to merge single-denotations. */ - def mergeSingleDenot(denot1: SingleDenotation, denot2: SingleDenotation): SingleDenotation = { - val info1 = denot1.info - val info2 = denot2.info - val sym1 = denot1.symbol - val sym2 = denot2.symbol - - val sym2Accessible = sym2.isAccessibleFrom(pre) - - /** Does `sym1` come before `sym2` in the linearization of `pre`? */ - def precedes(sym1: Symbol, sym2: Symbol) = { - def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match { - case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1) - case Nil => true - } - (sym1 ne sym2) && - (sym1.derivesFrom(sym2) || - !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses)) - } - - /** Similar to SymDenotation#accessBoundary, but without the special cases. */ - def accessBoundary(sym: Symbol) = - if (sym.is(Private)) sym.owner - else sym.privateWithin.orElse( - if (sym.is(Protected)) sym.owner.enclosingPackageClass - else defn.RootClass) - - /** Establish a partial order "preference" order between symbols. - * Give preference to `sym1` over `sym2` if one of the following - * conditions holds, in decreasing order of weight: - * 1. sym1 is concrete and sym2 is abstract - * 2. The owner of sym1 comes before the owner of sym2 in the linearization - * of the type of the prefix `pre`. - * 3. The access boundary of sym2 is properly contained in the access - * boundary of sym1. For protected access, we count the enclosing - * package as access boundary. - * 4. sym1 a method but sym2 is not. - * The aim of these criteria is to give some disambiguation on access which - * - does not depend on textual order or other arbitrary choices - * - minimizes raising of doubleDef errors - */ - def preferSym(sym1: Symbol, sym2: Symbol) = - sym1.eq(sym2) || - sym1.isAsConcrete(sym2) && - (!sym2.isAsConcrete(sym1) || - precedes(sym1.owner, sym2.owner) || - accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) || - sym1.is(Method) && !sym2.is(Method)) || - sym1.info.isErroneous - - /** Sym preference provided types also override */ - def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = - preferSym(sym1, sym2) && info1.overrides(info2) - - def handleDoubleDef = - if (preferSym(sym1, sym2)) denot1 - else if (preferSym(sym2, sym1)) denot2 - else doubleDefError(denot1, denot2, pre) - - if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2 - else { - val sym1Accessible = sym1.isAccessibleFrom(pre) - if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1 - else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 - else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 - else if (isDoubleDef(sym1, sym2)) handleDoubleDef - else { - val sym = - if (!sym1.exists) sym2 - else if (!sym2.exists) sym1 - else if (preferSym(sym2, sym1)) sym2 - else sym1 - val jointInfo = - try infoMeet(info1, info2) - catch { - case ex: MergeError => - if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode) - info1 // follow Scala2 linearization - - // compare with way merge is performed in SymDenotation#computeMembersNamed - else - throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2) - } - new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor) - } - } - } - - if (this eq that) this - else if (!this.exists) that - else if (!that.exists) this - else that match { - case that: SingleDenotation => - val r = mergeDenot(this, that) - if (r.exists) r else MultiDenotation(this, that) - case that @ MultiDenotation(denot1, denot2) => - this & (denot1, pre) & (denot2, pre) - } - } - - /** Form a choice between this denotation and that one. - * @param pre The prefix type of the members of the denotation, used - * to determine an accessible symbol if it exists. - */ - def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = { - - /** Normally, `tp1 | tp2`. Special cases for matching methods and classes, with - * the possibility of raising a merge error. - */ - def infoJoin(tp1: Type, tp2: Type): Type = tp1 match { - case tp1: TypeBounds => - tp2 match { - case tp2: TypeBounds => tp1 | tp2 - case tp2: ClassInfo if tp1 contains tp2 => tp1 - case _ => mergeConflict(tp1, tp2) - } - case tp1: ClassInfo => - tp2 match { - case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix | tp2.prefix) - case tp2: TypeBounds if tp2 contains tp1 => tp2 - case _ => mergeConflict(tp1, tp2) - } - case tp1 @ MethodType(names1, formals1) => - tp2 match { - case tp2 @ MethodType(names2, formals2) - if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedMethodType( - mergeNames(names1, names2, nme.syntheticParamName), - formals1, tp1.resultType | tp2.resultType.subst(tp2, tp1)) - case _ => - mergeConflict(tp1, tp2) - } - case tp1: PolyType => - tp2 match { - case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) => - tp1.derivedPolyType( - mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), - tp1.paramBounds, tp1.resultType | tp2.resultType.subst(tp2, tp1)) - case _ => - mergeConflict(tp1, tp2) - } - case _ => - tp1 | tp2 - } - - def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation = - if (denot1.matches(denot2)) { - val sym1 = denot1.symbol - val sym2 = denot2.symbol - val info1 = denot1.info - val info2 = denot2.info - val sameSym = sym1 eq sym2 - if (sameSym && (info1 frozen_<:< info2)) denot2 - else if (sameSym && (info2 frozen_<:< info1)) denot1 - else { - val jointSym = - if (sameSym) sym1 - else { - val owner2 = if (sym2 ne NoSymbol) sym2.owner else NoSymbol - /** Determine a symbol which is overridden by both sym1 and sym2. - * Preference is given to accessible symbols. - */ - def lubSym(overrides: Iterator[Symbol], previous: Symbol): Symbol = - if (!overrides.hasNext) previous - else { - val candidate = overrides.next - if (owner2 derivesFrom candidate.owner) - if (candidate isAccessibleFrom pre) candidate - else lubSym(overrides, previous orElse candidate) - else - lubSym(overrides, previous) - } - lubSym(sym1.allOverriddenSymbols, NoSymbol) - } - new JointRefDenotation( - jointSym, infoJoin(info1, info2), denot1.validFor & denot2.validFor) - } - } - else NoDenotation - - if (this eq that) this - else if (!this.exists) this - else if (!that.exists) that - else this match { - case denot1 @ MultiDenotation(denot11, denot12) => - denot1.derivedMultiDenotation(denot11 | (that, pre), denot12 | (that, pre)) - case denot1: SingleDenotation => - that match { - case denot2 @ MultiDenotation(denot21, denot22) => - denot2.derivedMultiDenotation(this | (denot21, pre), this | (denot22, pre)) - case denot2: SingleDenotation => - unionDenot(denot1, denot2) - } - } - } - - final def asSingleDenotation = asInstanceOf[SingleDenotation] - final def asSymDenotation = asInstanceOf[SymDenotation] - - def toText(printer: Printer): Text = printer.toText(this) - } - - /** An overloaded denotation consisting of the alternatives of both given denotations. - */ - case class MultiDenotation(denot1: Denotation, denot2: Denotation) extends Denotation(NoSymbol) { - final def infoOrCompleter = multiHasNot("info") - final def info(implicit ctx: Context) = infoOrCompleter - final def validFor = denot1.validFor & denot2.validFor - final def isType = false - final def signature(implicit ctx: Context) = Signature.OverloadedSignature - def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): Denotation = - derivedMultiDenotation(denot1.atSignature(sig, site, relaxed), denot2.atSignature(sig, site, relaxed)) - def current(implicit ctx: Context): Denotation = - derivedMultiDenotation(denot1.current, denot2.current) - def altsWith(p: Symbol => Boolean): List[SingleDenotation] = - denot1.altsWith(p) ++ denot2.altsWith(p) - def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation = { - val sd1 = denot1.suchThat(p) - val sd2 = denot2.suchThat(p) - if (sd1.exists) - if (sd2.exists) - if (isDoubleDef(denot1.symbol, denot2.symbol)) doubleDefError(denot1, denot2) - else throw new TypeError(s"failure to disambiguate overloaded reference $this") - else sd1 - else sd2 - } - def hasAltWith(p: SingleDenotation => Boolean): Boolean = - denot1.hasAltWith(p) || denot2.hasAltWith(p) - def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = { - val d1 = denot1 accessibleFrom (pre, superAccess) - val d2 = denot2 accessibleFrom (pre, superAccess) - if (!d1.exists) d2 - else if (!d2.exists) d1 - else derivedMultiDenotation(d1, d2) - } - def mapInfo(f: Type => Type)(implicit ctx: Context): Denotation = - derivedMultiDenotation(denot1.mapInfo(f), denot2.mapInfo(f)) - def derivedMultiDenotation(d1: Denotation, d2: Denotation) = - if ((d1 eq denot1) && (d2 eq denot2)) this else MultiDenotation(d1, d2) - override def toString = alternatives.mkString(" <and> ") - - private def multiHasNot(op: String): Nothing = - throw new UnsupportedOperationException( - s"multi-denotation with alternatives $alternatives does not implement operation $op") - } - - /** A non-overloaded denotation */ - abstract class SingleDenotation(symbol: Symbol) extends Denotation(symbol) with PreDenotation { - def hasUniqueSym: Boolean - protected def newLikeThis(symbol: Symbol, info: Type): SingleDenotation - - final def signature(implicit ctx: Context): Signature = { - if (isType) Signature.NotAMethod // don't force info if this is a type SymDenotation - else info match { - case info: MethodicType => - try info.signature - catch { // !!! DEBUG - case scala.util.control.NonFatal(ex) => - ctx.echo(s"cannot take signature of ${info.show}") - throw ex - } - case _ => Signature.NotAMethod - } - } - - def derivedSingleDenotation(symbol: Symbol, info: Type)(implicit ctx: Context): SingleDenotation = - if ((symbol eq this.symbol) && (info eq this.info)) this - else newLikeThis(symbol, info) - - def mapInfo(f: Type => Type)(implicit ctx: Context): SingleDenotation = - derivedSingleDenotation(symbol, f(info)) - - def orElse(that: => SingleDenotation) = if (this.exists) this else that - - def altsWith(p: Symbol => Boolean): List[SingleDenotation] = - if (exists && p(symbol)) this :: Nil else Nil - - def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation = - if (exists && p(symbol)) this else NoDenotation - - def hasAltWith(p: SingleDenotation => Boolean): Boolean = - exists && p(this) - - def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = - if (!symbol.exists || symbol.isAccessibleFrom(pre, superAccess)) this else NoDenotation - - def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation = { - val situated = if (site == NoPrefix) this else asSeenFrom(site) - val matches = sig.matchDegree(situated.signature) >= - (if (relaxed) Signature.ParamMatch else Signature.FullMatch) - if (matches) this else NoDenotation - } - - // ------ Forming types ------------------------------------------- - - /** The TypeRef representing this type denotation at its original location. */ - def typeRef(implicit ctx: Context): TypeRef = - TypeRef(symbol.owner.thisType, symbol.name.asTypeName, this) - - /** The TermRef representing this term denotation at its original location. */ - def termRef(implicit ctx: Context): TermRef = - TermRef(symbol.owner.thisType, symbol.name.asTermName, this) - - /** The TermRef representing this term denotation at its original location - * and at signature `NotAMethod`. - */ - def valRef(implicit ctx: Context): TermRef = - TermRef.withSigAndDenot(symbol.owner.thisType, symbol.name.asTermName, Signature.NotAMethod, this) - - /** The TermRef representing this term denotation at its original location - * at the denotation's signature. - * @note Unlike `valRef` and `termRef`, this will force the completion of the - * denotation via a call to `info`. - */ - def termRefWithSig(implicit ctx: Context): TermRef = - TermRef.withSigAndDenot(symbol.owner.thisType, symbol.name.asTermName, signature, this) - - /** The NamedType representing this denotation at its original location. - * Same as either `typeRef` or `termRefWithSig` depending whether this denotes a type or not. - */ - def namedType(implicit ctx: Context): NamedType = - if (isType) typeRef else termRefWithSig - - // ------ Transformations ----------------------------------------- - - private[this] var myValidFor: Period = Nowhere - - def validFor = myValidFor - def validFor_=(p: Period) = - myValidFor = p - - /** The next SingleDenotation in this run, with wrap-around from last to first. - * - * There may be several `SingleDenotation`s with different validity - * representing the same underlying definition at different phases. - * These are called a "flock". Flock members are generated by - * @See current. Flock members are connected in a ring - * with their `nextInRun` fields. - * - * There are the following invariants concerning flock members - * - * 1) validity periods are non-overlapping - * 2) the union of all validity periods is a contiguous - * interval. - */ - protected var nextInRun: SingleDenotation = this - - /** The version of this SingleDenotation that was valid in the first phase - * of this run. - */ - def initial: SingleDenotation = - if (validFor == Nowhere) this - else { - var current = nextInRun - while (current.validFor.code > this.myValidFor.code) current = current.nextInRun - current - } - - def history: List[SingleDenotation] = { - val b = new ListBuffer[SingleDenotation] - var current = initial - do { - b += (current) - current = current.nextInRun - } - while (current ne initial) - b.toList - } - - /** Invalidate all caches and fields that depend on base classes and their contents */ - def invalidateInheritedInfo(): Unit = () - - /** Move validity period of this denotation to a new run. Throw a StaleSymbol error - * if denotation is no longer valid. - */ - private def bringForward()(implicit ctx: Context): SingleDenotation = this match { - case denot: SymDenotation if ctx.stillValid(denot) => - assert(ctx.runId > validFor.runId || ctx.settings.YtestPickler.value, // mixing test pickler with debug printing can travel back in time - s"denotation $denot invalid in run ${ctx.runId}. ValidFor: $validFor") - var d: SingleDenotation = denot - do { - d.validFor = Period(ctx.period.runId, d.validFor.firstPhaseId, d.validFor.lastPhaseId) - d.invalidateInheritedInfo() - d = d.nextInRun - } while (d ne denot) - this - case _ => - if (coveredInterval.containsPhaseId(ctx.phaseId)) { - if (ctx.debug) ctx.traceInvalid(this) - staleSymbolError - } - else NoDenotation - } - - /** Produce a denotation that is valid for the given context. - * Usually called when !(validFor contains ctx.period) - * (even though this is not a precondition). - * If the runId of the context is the same as runId of this denotation, - * the right flock member is located, or, if it does not exist yet, - * created by invoking a transformer (@See Transformers). - * If the runId's differ, but this denotation is a SymDenotation - * and its toplevel owner class or module - * is still a member of its enclosing package, then the whole flock - * is brought forward to be valid in the new runId. Otherwise - * the symbol is stale, which constitutes an internal error. - */ - def current(implicit ctx: Context): SingleDenotation = { - val currentPeriod = ctx.period - val valid = myValidFor - if (valid.code <= 0) { - // can happen if we sit on a stale denotation which has been replaced - // wholesale by an installAfter; in this case, proceed to the next - // denotation and try again. - if (validFor == Nowhere && nextInRun.validFor != Nowhere) return nextInRun.current - assert(false) - } - - if (valid.runId != currentPeriod.runId) - if (exists) initial.bringForward.current - else this - else { - var cur = this - if (currentPeriod.code > valid.code) { - // search for containing period as long as nextInRun increases. - var next = nextInRun - while (next.validFor.code > valid.code && !(next.validFor contains currentPeriod)) { - cur = next - next = next.nextInRun - } - if (next.validFor.code > valid.code) { - // in this case, next.validFor contains currentPeriod - cur = next - cur - } else { - //println(s"might need new denot for $cur, valid for ${cur.validFor} at $currentPeriod") - // not found, cur points to highest existing variant - val nextTransformerId = ctx.nextDenotTransformerId(cur.validFor.lastPhaseId) - if (currentPeriod.lastPhaseId <= nextTransformerId) - cur.validFor = Period(currentPeriod.runId, cur.validFor.firstPhaseId, nextTransformerId) - else { - var startPid = nextTransformerId + 1 - val transformer = ctx.denotTransformers(nextTransformerId) - //println(s"transforming $this with $transformer") - try { - next = transformer.transform(cur)(ctx.withPhase(transformer)).syncWithParents - } catch { - case ex: CyclicReference => - println(s"error while transforming $this") // DEBUG - throw ex - } - if (next eq cur) - startPid = cur.validFor.firstPhaseId - else { - next match { - case next: ClassDenotation => - assert(!next.is(Package), s"illegal transformation of package denotation by transformer ${ctx.withPhase(transformer).phase}") - next.resetFlag(Frozen) - case _ => - } - next.insertAfter(cur) - cur = next - } - cur.validFor = Period(currentPeriod.runId, startPid, transformer.lastPhaseId) - //printPeriods(cur) - //println(s"new denot: $cur, valid for ${cur.validFor}") - } - cur.current // multiple transformations could be required - } - } else { - // currentPeriod < end of valid; in this case a version must exist - // but to be defensive we check for infinite loop anyway - var cnt = 0 - while (!(cur.validFor contains currentPeriod)) { - //println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}") - cur = cur.nextInRun - // Note: One might be tempted to add a `prev` field to get to the new denotation - // more directly here. I tried that, but it degrades rather than improves - // performance: Test setup: Compile everything in dotc and immediate subdirectories - // 10 times. Best out of 10: 18154ms with `prev` field, 17777ms without. - cnt += 1 - if (cnt > MaxPossiblePhaseId) - return current(ctx.withPhase(coveredInterval.firstPhaseId)) - } - cur - } - } - } - - private def demandOutsideDefinedMsg(implicit ctx: Context): String = - s"demanding denotation of $this at phase ${ctx.phase}(${ctx.phaseId}) outside defined interval: defined periods are${definedPeriodsString}" - - /** Install this denotation to be the result of the given denotation transformer. - * This is the implementation of the same-named method in SymDenotations. - * It's placed here because it needs access to private fields of SingleDenotation. - * @pre Can only be called in `phase.next`. - */ - protected def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = { - val targetId = phase.next.id - if (ctx.phaseId != targetId) installAfter(phase)(ctx.withPhase(phase.next)) - else { - val current = symbol.current - // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}") - // printPeriods(current) - this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) - if (current.validFor.firstPhaseId >= targetId) - insertInsteadOf(current) - else { - current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) - insertAfter(current) - } - // printPeriods(this) - } - } - - /** Apply a transformation `f` to all denotations in this group that start at or after - * given phase. Denotations are replaced while keeping the same validity periods. - */ - protected def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = { - var current = symbol.current - while (current.validFor.firstPhaseId < phase.id && (current.nextInRun.validFor.code > current.validFor.code)) - current = current.nextInRun - var hasNext = true - while ((current.validFor.firstPhaseId >= phase.id) && hasNext) { - val current1: SingleDenotation = f(current.asSymDenotation) - if (current1 ne current) { - current1.validFor = current.validFor - current1.insertInsteadOf(current) - } - hasNext = current1.nextInRun.validFor.code > current1.validFor.code - current = current1.nextInRun - } - } - - /** Insert this denotation so that it follows `prev`. */ - private def insertAfter(prev: SingleDenotation) = { - this.nextInRun = prev.nextInRun - prev.nextInRun = this - } - - /** Insert this denotation instead of `old`. - * Also ensure that `old` refers with `nextInRun` to this denotation - * and set its `validFor` field to `NoWhere`. This is necessary so that - * references to the old denotation can be brought forward via `current` - * to a valid denotation. - * - * The code to achieve this is subtle in that it works correctly - * whether the replaced denotation is the only one in its cycle or not. - */ - private def insertInsteadOf(old: SingleDenotation): Unit = { - var prev = old - while (prev.nextInRun ne old) prev = prev.nextInRun - // order of next two assignments is important! - prev.nextInRun = this - this.nextInRun = old.nextInRun - old.validFor = Nowhere - } - - def staleSymbolError(implicit ctx: Context) = { - def ownerMsg = this match { - case denot: SymDenotation => s"in ${denot.owner}" - case _ => "" - } - def msg = s"stale symbol; $this#${symbol.id} $ownerMsg, defined in ${myValidFor}, is referred to in run ${ctx.period}" - throw new StaleSymbol(msg) - } - - /** The period (interval of phases) for which there exists - * a valid denotation in this flock. - */ - def coveredInterval(implicit ctx: Context): Period = { - var cur = this - var cnt = 0 - var interval = validFor - do { - cur = cur.nextInRun - cnt += 1 - assert(cnt <= MaxPossiblePhaseId, demandOutsideDefinedMsg) - interval |= cur.validFor - } while (cur ne this) - interval - } - - /** For ClassDenotations only: - * If caches influenced by parent classes are still valid, the denotation - * itself, otherwise a freshly initialized copy. - */ - def syncWithParents(implicit ctx: Context): SingleDenotation = this - - /** Show declaration string; useful for showing declarations - * as seen from subclasses. - */ - def showDcl(implicit ctx: Context): String = ctx.dclText(this).show - - override def toString = - if (symbol == NoSymbol) symbol.toString - else s"<SingleDenotation of type $infoOrCompleter>" - - def definedPeriodsString: String = { - var sb = new StringBuilder() - var cur = this - var cnt = 0 - do { - sb.append(" " + cur.validFor) - cur = cur.nextInRun - cnt += 1 - if (cnt > MaxPossiblePhaseId) { sb.append(" ..."); cur = this } - } while (cur ne this) - sb.toString - } - - // ------ PreDenotation ops ---------------------------------------------- - - final def first = this - final def last = this - final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this - final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym) - final def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = { - val d = signature.matchDegree(other.signature) - d == Signature.FullMatch || - d >= Signature.ParamMatch && info.matches(other.info) - } - final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation = - if (p(this)) this else NoDenotation - final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation = - if (denots.exists && denots.matches(this)) NoDenotation else this - def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation = - if (hasUniqueSym && prevDenots.containsSym(symbol)) NoDenotation - else if (isType) filterDisjoint(ownDenots).asSeenFrom(pre) - else asSeenFrom(pre).filterDisjoint(ownDenots) - final def filterExcluded(excluded: FlagSet)(implicit ctx: Context): SingleDenotation = - if (excluded.isEmpty || !(this overlaps excluded)) this else NoDenotation - - type AsSeenFromResult = SingleDenotation - protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): SingleDenotation = { - val symbol = this.symbol - val owner = this match { - case thisd: SymDenotation => thisd.owner - case _ => if (symbol.exists) symbol.owner else NoSymbol - } - if (!owner.membersNeedAsSeenFrom(pre)) this - else derivedSingleDenotation(symbol, info.asSeenFrom(pre, owner)) - } - - private def overlaps(fs: FlagSet)(implicit ctx: Context): Boolean = this match { - case sd: SymDenotation => sd is fs - case _ => symbol is fs - } - } - - abstract class NonSymSingleDenotation(symbol: Symbol) extends SingleDenotation(symbol) { - def infoOrCompleter: Type - def info(implicit ctx: Context) = infoOrCompleter - def isType = infoOrCompleter.isInstanceOf[TypeType] - } - - class UniqueRefDenotation( - symbol: Symbol, - val infoOrCompleter: Type, - initValidFor: Period) extends NonSymSingleDenotation(symbol) { - validFor = initValidFor - override def hasUniqueSym: Boolean = true - protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = new UniqueRefDenotation(s, i, validFor) - } - - class JointRefDenotation( - symbol: Symbol, - val infoOrCompleter: Type, - initValidFor: Period) extends NonSymSingleDenotation(symbol) { - validFor = initValidFor - override def hasUniqueSym = false - protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = new JointRefDenotation(s, i, validFor) - } - - class ErrorDenotation(implicit ctx: Context) extends NonSymSingleDenotation(NoSymbol) { - override def exists = false - override def hasUniqueSym = false - def infoOrCompleter = NoType - validFor = Period.allInRun(ctx.runId) - protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = this - } - - /** An error denotation that provides more info about the missing reference. - * Produced by staticRef, consumed by requiredSymbol. - */ - case class MissingRef(val owner: SingleDenotation, name: Name)(implicit ctx: Context) extends ErrorDenotation { - val ex: Exception = new Exception - } - - /** An error denotation that provides more info about alternatives - * that were found but that do not qualify. - * Produced by staticRef, consumed by requiredSymbol. - */ - case class NoQualifyingRef(alts: List[SingleDenotation])(implicit ctx: Context) extends ErrorDenotation - - /** A double definition - */ - def isDoubleDef(sym1: Symbol, sym2: Symbol)(implicit ctx: Context): Boolean = - (sym1.exists && sym2.exists && - (sym1 ne sym2) && (sym1.owner eq sym2.owner) && - !sym1.is(Bridge) && !sym2.is(Bridge)) - - def doubleDefError(denot1: Denotation, denot2: Denotation, pre: Type = NoPrefix)(implicit ctx: Context): Nothing = { - val sym1 = denot1.symbol - val sym2 = denot2.symbol - def fromWhere = if (pre == NoPrefix) "" else i"\nwhen seen as members of $pre" - throw new MergeError( - i"""cannot merge - | $sym1: ${sym1.info} and - | $sym2: ${sym2.info}; - |they are both defined in ${sym1.owner} but have matching signatures - | ${denot1.info} and - | ${denot2.info}$fromWhere""", - denot2.info, denot2.info) - } - - // --------------- PreDenotations ------------------------------------------------- - - /** A PreDenotation represents a group of single denotations - * It is used as an optimization to avoid forming MultiDenotations too eagerly. - */ - trait PreDenotation { - - /** A denotation in the group exists */ - def exists: Boolean - - /** First/last denotation in the group */ - def first: Denotation - def last: Denotation - - /** Convert to full denotation by &-ing all elements */ - def toDenot(pre: Type)(implicit ctx: Context): Denotation - - /** Group contains a denotation that refers to given symbol */ - def containsSym(sym: Symbol): Boolean - - /** Group contains a denotation with given signature */ - def matches(other: SingleDenotation)(implicit ctx: Context): Boolean - - /** Keep only those denotations in this group which satisfy predicate `p`. */ - def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation - - /** Keep only those denotations in this group which have a signature - * that's not already defined by `denots`. - */ - def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation - - /** Keep only those inherited members M of this predenotation for which the following is true - * - M is not marked Private - * - If M has a unique symbol, it does not appear in `prevDenots`. - * - M's signature as seen from prefix `pre` does not appear in `ownDenots` - * Return the denotation as seen from `pre`. - * Called from SymDenotations.computeMember. There, `ownDenots` are the denotations found in - * the base class, which shadow any inherited denotations with the same signature. - * `prevDenots` are the denotations that are defined in the class or inherited from - * a base type which comes earlier in the linearization. - */ - def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation - - /** Keep only those denotations in this group whose flags do not intersect - * with `excluded`. - */ - def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation - - private var cachedPrefix: Type = _ - private var cachedAsSeenFrom: AsSeenFromResult = _ - private var validAsSeenFrom: Period = Nowhere - type AsSeenFromResult <: PreDenotation - - /** The denotation with info(s) as seen from prefix type */ - final def asSeenFrom(pre: Type)(implicit ctx: Context): AsSeenFromResult = - if (Config.cacheAsSeenFrom) { - if ((cachedPrefix ne pre) || ctx.period != validAsSeenFrom) { - cachedAsSeenFrom = computeAsSeenFrom(pre) - cachedPrefix = pre - validAsSeenFrom = ctx.period - } - cachedAsSeenFrom - } else computeAsSeenFrom(pre) - - protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): AsSeenFromResult - - /** The union of two groups. */ - def union(that: PreDenotation) = - if (!this.exists) that - else if (!that.exists) this - else DenotUnion(this, that) - } - - final case class DenotUnion(denots1: PreDenotation, denots2: PreDenotation) extends PreDenotation { - assert(denots1.exists && denots2.exists, s"Union of non-existing denotations ($denots1) and ($denots2)") - def exists = true - def first = denots1.first - def last = denots2.last - def toDenot(pre: Type)(implicit ctx: Context) = - (denots1 toDenot pre) & (denots2 toDenot pre, pre) - def containsSym(sym: Symbol) = - (denots1 containsSym sym) || (denots2 containsSym sym) - def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = - denots1.matches(other) || denots2.matches(other) - def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation = - derivedUnion(denots1 filterWithPredicate p, denots2 filterWithPredicate p) - def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation = - derivedUnion(denots1 filterDisjoint denots, denots2 filterDisjoint denots) - def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation = - derivedUnion(denots1.mapInherited(ownDenots, prevDenots, pre), denots2.mapInherited(ownDenots, prevDenots, pre)) - def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation = - derivedUnion(denots1.filterExcluded(excluded), denots2.filterExcluded(excluded)) - - type AsSeenFromResult = PreDenotation - protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): PreDenotation = - derivedUnion(denots1.asSeenFrom(pre), denots2.asSeenFrom(pre)) - private def derivedUnion(denots1: PreDenotation, denots2: PreDenotation) = - if ((denots1 eq this.denots1) && (denots2 eq this.denots2)) this - else denots1 union denots2 - } - - // --------------- Context Base Trait ------------------------------- - - trait DenotationsBase { this: ContextBase => - - /** The current denotation of the static reference given by path, - * or a MissingRef or NoQualifyingRef instance, if it does not exist. - * if generateStubs is set, generates stubs for missing top-level symbols - */ - def staticRef(path: Name, generateStubs: Boolean = true)(implicit ctx: Context): Denotation = { - def recur(path: Name, len: Int): Denotation = { - val point = path.lastIndexOf('.', len - 1) - val owner = - if (point > 0) recur(path.toTermName, point).disambiguate(_.info.isParameterless) - else if (path.isTermName) defn.RootClass.denot - else defn.EmptyPackageClass.denot - if (owner.exists) { - val name = path slice (point + 1, len) - val result = owner.info.member(name) - if (result ne NoDenotation) result - else { - val alt = - if (generateStubs) missingHook(owner.symbol.moduleClass, name) - else NoSymbol - if (alt.exists) alt.denot - else MissingRef(owner, name) - } - } - else owner - } - recur(path, path.length) - } - - /** If we are looking for a non-existing term name in a package, - * assume it is a package for which we do not have a directory and - * enter it. - */ - def missingHook(owner: Symbol, name: Name)(implicit ctx: Context): Symbol = - if ((owner is Package) && name.isTermName) - ctx.newCompletePackageSymbol(owner, name.asTermName).entered - else - NoSymbol - } - - /** An exception for accessing symbols that are no longer valid in current run */ - class StaleSymbol(msg: => String) extends Exception { - util.Stats.record("stale symbol") - override def getMessage() = msg - } -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala deleted file mode 100644 index 63fbc98dc..000000000 --- a/src/dotty/tools/dotc/core/Flags.scala +++ /dev/null @@ -1,640 +0,0 @@ -package dotty.tools.dotc.core - -import language.implicitConversions - -object Flags { - - /** A FlagSet represents a set of flags. Flags are encoded as follows: - * The first two bits indicate whether a flagset applies to terms, - * to types, or to both. Bits 2..63 are available for properties - * and can be doubly used for terms and types. - * Combining two FlagSets with `|` will give a FlagSet - * that has the intersection of the applicability to terms/types - * of the two flag sets. It is checked that the intersection is not empty. - */ - case class FlagSet(val bits: Long) extends AnyVal { - - /** The union of this flag set and the given flag set - */ - def | (that: FlagSet): FlagSet = - if (bits == 0) that - else if (that.bits == 0) this - else { - val tbits = bits & that.bits & KINDFLAGS - assert(tbits != 0, s"illegal flagset combination: $this and $that") - FlagSet(tbits | ((this.bits | that.bits) & ~KINDFLAGS)) - } - - /** The intersection of this flag set and the given flag set */ - def & (that: FlagSet) = FlagSet(bits & that.bits) - - /** The intersection of this flag set with the complement of the given flag set */ - def &~ (that: FlagSet) = { - val tbits = bits & KINDFLAGS - if ((tbits & that.bits) == 0) this - else FlagSet(tbits | ((this.bits & ~that.bits) & ~KINDFLAGS)) - } - - /** Does this flag set have a non-empty intersection with the given flag set? - * This means that both the kind flags and the carrier bits have non-empty intersection. - */ - def is(flags: FlagSet): Boolean = { - val fs = bits & flags.bits - (fs & KINDFLAGS) != 0 && (fs & ~KINDFLAGS) != 0 - } - - /** Does this flag set have a non-empty intersection with the given flag set, - * and at the same time contain none of the flags in the `butNot` set? - */ - def is(flags: FlagSet, butNot: FlagSet): Boolean = is(flags) && !is(butNot) - - /** Does this flag set have all of the flags in given flag conjunction? - * Pre: The intersection of the typeflags of both sets must be non-empty. - */ - def is(flags: FlagConjunction): Boolean = { - val fs = bits & flags.bits - (fs & KINDFLAGS) != 0 && - (fs >>> TYPESHIFT) == (flags.bits >>> TYPESHIFT) - } - - /** Does this flag set have all of the flags in given flag conjunction? - * and at the same time contain none of the flags in the `butNot` set? - * Pre: The intersection of the typeflags of both sets must be non-empty. - */ - def is(flags: FlagConjunction, butNot: FlagSet): Boolean = is(flags) && !is(butNot) - - def isEmpty = (bits & ~KINDFLAGS) == 0 - - /** Is this flag set a subset of that one? */ - def <= (that: FlagSet) = (bits & that.bits) == bits - - /** Does this flag set apply to terms? */ - def isTermFlags = (bits & TERMS) != 0 - - /** Does this flag set apply to terms? */ - def isTypeFlags = (bits & TYPES) != 0 - - /** This flag set with all flags transposed to be type flags */ - def toTypeFlags = if (bits == 0) this else FlagSet(bits & ~KINDFLAGS | TYPES) - - /** This flag set with all flags transposed to be term flags */ - def toTermFlags = if (bits == 0) this else FlagSet(bits & ~KINDFLAGS | TERMS) - - /** This flag set with all flags transposed to be common flags */ - def toCommonFlags = if (bits == 0) this else FlagSet(bits | KINDFLAGS) - - /** The number of non-kind flags in this set */ - def numFlags: Int = java.lang.Long.bitCount(bits & ~KINDFLAGS) - - /** The lowest non-kind bit set in this flagset */ - def firstBit: Int = java.lang.Long.numberOfTrailingZeros(bits & ~KINDFLAGS) - - /** The list of non-empty names of flags with given index idx that are set in this FlagSet */ - private def flagString(idx: Int): List[String] = - if ((bits & (1L << idx)) == 0) Nil - else { - def halfString(kind: Int) = - if ((bits & (1L << kind)) != 0) flagName(idx)(kind) else "" - val termFS = halfString(TERMindex) - val typeFS = halfString(TYPEindex) - val strs = termFS :: (if (termFS == typeFS) Nil else typeFS :: Nil) - strs filter (_.nonEmpty) - } - - /** The list of non-empty names of flags that are set in this FlagSet */ - def flagStrings: Seq[String] = { - val rawStrings = (2 to MaxFlag).flatMap(flagString) - if (this is Local) - rawStrings.filter(_ != "<local>").map { - case "private" => "private[this]" - case "protected" => "protected[this]" - case str => str - } - else rawStrings - } - - /** The string representation of this flag set */ - override def toString = flagStrings.mkString(" ") - } - - /** A class representing flag sets that should be tested - * conjunctively. I.e. for a flag conjunction `fc`, - * `x is fc` tests whether `x` contains all flags in `fc`. - */ - case class FlagConjunction(bits: Long) { - override def toString = FlagSet(bits).toString - } - - private final val TYPESHIFT = 2 - private final val TERMindex = 0 - private final val TYPEindex = 1 - private final val TERMS = 1 << TERMindex - private final val TYPES = 1 << TYPEindex - private final val KINDFLAGS = TERMS | TYPES - - private final val FirstFlag = 2 - private final val FirstNotPickledFlag = 48 - private final val MaxFlag = 63 - - private val flagName = Array.fill(64, 2)("") - - private def isDefinedAsFlag(idx: Int) = flagName(idx) exists (_.nonEmpty) - - /** The flag set containing all defined flags of either kind whose bits - * lie in the given range - */ - private def flagRange(start: Int, end: Int) = - FlagSet((KINDFLAGS.toLong /: (start until end)) ((bits, idx) => - if (isDefinedAsFlag(idx)) bits | (1L << idx) else bits)) - - /** The flag with given index between 2 and 63 which applies to terms. - * Installs given name as the name of the flag. */ - private def termFlag(index: Int, name: String): FlagSet = { - flagName(index)(TERMindex) = name - FlagSet(TERMS | (1L << index)) - } - - /** The flag with given index between 2 and 63 which applies to types. - * Installs given name as the name of the flag. */ - private def typeFlag(index: Int, name: String): FlagSet = { - flagName(index)(TYPEindex) = name - FlagSet(TYPES | (1L << index)) - } - - /** The flag with given index between 2 and 63 which applies to both terms and types - * Installs given name as the name of the flag. */ - private def commonFlag(index: Int, name: String): FlagSet = { - flagName(index)(TERMindex) = name - flagName(index)(TYPEindex) = name - FlagSet(TERMS | TYPES | (1L << index)) - } - - /** The union of all flags in given flag set */ - def union(flagss: FlagSet*) = (EmptyFlags /: flagss)(_ | _) - - /** The conjunction of all flags in given flag set */ - def allOf(flagss: FlagSet*) = { - assert(flagss forall (_.numFlags == 1), "Flags.allOf doesn't support flag " + flagss.find(_.numFlags != 1)) - FlagConjunction(union(flagss: _*).bits) - } - - def commonFlags(flagss: FlagSet*) = union(flagss.map(_.toCommonFlags): _*) - - /** The empty flag set */ - final val EmptyFlags = FlagSet(0) - - /** The undefined flag set */ - final val UndefinedFlags = FlagSet(~KINDFLAGS) - - // Available flags: - - /** Labeled with `private` modifier */ - final val Private = commonFlag(2, "private") - final val PrivateTerm = Private.toTermFlags - final val PrivateType = Private.toTypeFlags - - /** Labeled with `protected` modifier */ - final val Protected = commonFlag(3, "protected") - - /** Labeled with `override` modifier */ - final val Override = commonFlag(4, "override") - - /** A declared, but not defined member */ - final val Deferred = commonFlag(5, "<deferred>") - final val DeferredTerm = Deferred.toTermFlags - final val DeferredType = Deferred.toTypeFlags - - /** Labeled with `final` modifier */ - final val Final = commonFlag(6, "final") - - /** A method symbol. */ - final val MethodOrHKCommon = commonFlag(7, "<method>") - final val Method = MethodOrHKCommon.toTermFlags - final val HigherKinded = MethodOrHKCommon.toTypeFlags - - /** A (term or type) parameter to a class or method */ - final val Param = commonFlag(8, "<param>") - final val TermParam = Param.toTermFlags - final val TypeParam = Param.toTypeFlags - - /** Labeled with `implicit` modifier (implicit value) */ - final val ImplicitCommon = commonFlag(9, "implicit") - final val Implicit = ImplicitCommon.toTermFlags - - /** Labeled with `lazy` (a lazy val). */ - final val Lazy = termFlag(10, "lazy") - - /** A trait */ - final val Trait = typeFlag(10, "<trait>") - - final val LazyOrTrait = Lazy.toCommonFlags - - /** A value or variable accessor (getter or setter) */ - final val Accessor = termFlag(11, "<accessor>") - - /** Labeled with `sealed` modifier (sealed class) */ - final val Sealed = typeFlag(11, "sealed") - - final val AccessorOrSealed = Accessor.toCommonFlags - - /** A mutable var */ - final val Mutable = termFlag(12, "mutable") - - /** Symbol is local to current class (i.e. private[this] or protected[this] - * pre: Private or Protected are also set - */ - final val Local = commonFlag(13, "<local>") - - /** A field generated for a primary constructor parameter (no matter if it's a 'val' or not), - * or an accessor of such a field. - */ - final val ParamAccessor = commonFlag(14, "<paramaccessor>") - final val TermParamAccessor = ParamAccessor.toTermFlags - final val TypeParamAccessor = ParamAccessor.toTypeFlags - - /** A value or class implementing a module */ - final val Module = commonFlag(15, "module") - final val ModuleVal = Module.toTermFlags - final val ModuleClass = Module.toTypeFlags - - /** A value or class representing a package */ - final val Package = commonFlag(16, "<package>") - final val PackageVal = Package.toTermFlags - final val PackageClass = Package.toTypeFlags - - /** A case class or its companion object */ - final val Case = commonFlag(17, "case") - final val CaseClass = Case.toTypeFlags - final val CaseVal = Case.toTermFlags - - /** A compiler-generated symbol, which is visible for type-checking - * (compare with artifact) - */ - final val Synthetic = commonFlag(18, "<synthetic>") - - /** Symbol's name is expanded */ - final val ExpandedName = commonFlag(19, "<expandedname>") - - /** A covariant type variable / an outer accessor */ - final val CovariantOrOuter = commonFlag(20, "") - final val Covariant = typeFlag(20, "<covariant>") - final val OuterAccessor = termFlag(20, "<outer accessor>") - - /** A contravariant type variable / a label method */ - final val ContravariantOrLabel = commonFlag(21, "") - final val Contravariant = typeFlag(21, "<contravariant>") - final val Label = termFlag(21, "<label>") - - - /** A trait that has only abstract methods as members - * (and therefore can be represented by a Java interface - */ - final val PureInterface = typeFlag(22, "interface") // TODO when unpickling, reconstitute from context - - /** Labeled with of abstract & override */ - final val AbsOverride = termFlag(22, "abstract override") - - /** Labeled with `abstract` modifier (an abstract class) - * Note: You should never see Abstract on any symbol except a class. - * Note: the flag counts as common, because it can be combined with OVERRIDE in a term. - */ - final val Abstract = commonFlag(23, "abstract") - - /** Lazy val or method is known or assumed to be stable and realizable */ - final val Stable = termFlag(24, "<stable>") - - /** A case parameter accessor */ - final val CaseAccessor = termFlag(25, "<caseaccessor>") - - /** A binding for a type parameter of a base class or trait. - * TODO: Replace with combination of isType, ExpandedName, and Override? - */ - final val BaseTypeArg = typeFlag(25, "<basetypearg>") - - final val CaseAccessorOrBaseTypeArg = CaseAccessor.toCommonFlags - - /** A super accessor */ - final val SuperAccessor = termFlag(26, "<superaccessor>") - - /** An unpickled Scala 2.x class */ - final val Scala2x = typeFlag(26, "<scala-2.x>") - - final val SuperAccessorOrScala2x = SuperAccessor.toCommonFlags - - /** A method that has default params */ - final val DefaultParameterized = termFlag(27, "<defaultparam>") - - /** A type that is defined by a type bind */ - final val BindDefinedType = typeFlag(27, "<bind-defined>") - - /** Symbol is inlined */ - final val Inline = commonFlag(29, "inline") - - /** Symbol is defined by a Java class */ - final val JavaDefined = commonFlag(30, "<java>") - - /** Symbol is implemented as a Java static */ - final val JavaStatic = commonFlag(31, "<static>") - final val JavaStaticTerm = JavaStatic.toTermFlags - final val JavaStaticType = JavaStatic.toTypeFlags - - /** Trait does not have fields or initialization code */ - final val NoInits = typeFlag(32, "<noInits>") - - /** Variable is accessed from nested function. */ - final val Captured = termFlag(32, "<captured>") - - /** Symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode */ - final val Artifact = commonFlag(33, "<artifact>") - - /** A bridge method. Set by Erasure */ - final val Bridge = termFlag(34, "<bridge>") - - /** All class attributes are fully defined */ - final val FullyCompleted = typeFlag(34, "<fully-completed>") - - /** Symbol is a Java varargs bridge */ // (needed?) - final val VBridge = termFlag(35, "<vbridge>") // TODO remove - - /** Symbol is a method which should be marked ACC_SYNCHRONIZED */ - final val Synchronized = termFlag(36, "<synchronized>") - - /** Symbol is a Java-style varargs method */ - final val JavaVarargs = termFlag(37, "<varargs>") - - /** Symbol is a Java default method */ - final val DefaultMethod = termFlag(38, "<defaultmethod>") - - /** Symbol is a Java enum */ - final val Enum = commonFlag(40, "<enum>") - - // Flags following this one are not pickled - - /** Symbol always defines a fresh named type */ - final val Fresh = commonFlag(45, "<fresh>") - - /** Symbol is defined in a super call */ - final val InSuperCall = commonFlag(46, "<in supercall>") - - /** Denotation is in train of being loaded and completed, used to catch cyclic dependencies */ - final val Touched = commonFlag(48, "<touched>") - - /** Class is not allowed to accept new members because fingerprint of subclass has been taken */ - final val Frozen = commonFlag(49, "<frozen>") - - /** An error symbol */ - final val Erroneous = commonFlag(50, "<is-error>") - - /** Class has been lifted out to package level, local value has been lifted out to class level */ - final val Lifted = commonFlag(51, "<lifted>") - - /** Term member has been mixed in */ - final val MixedIn = commonFlag(52, "<mixedin>") - - /** Symbol is a generated specialized member */ - final val Specialized = commonFlag(53, "<specialized>") - - /** Symbol is a self name */ - final val SelfName = termFlag(54, "<selfname>") - - /** Symbol is an implementation class of a Scala2 trait */ - final val ImplClass = typeFlag(54, "<implclass>") - - final val SelfNameOrImplClass = SelfName.toCommonFlags - - /** An existentially bound symbol (Scala 2.x only) */ - final val Scala2ExistentialCommon = commonFlag(55, "<existential>") - final val Scala2Existential = Scala2ExistentialCommon.toTypeFlags - - /** An overloaded symbol (Scala 2.x only) */ - final val Scala2Overloaded = termFlag(56, "<overloaded>") - - /** A module variable (Scala 2.x only) */ - final val Scala2ModuleVar = termFlag(57, "<modulevar>") - - /** A definition that's initialized before the super call (Scala 2.x only) */ - final val Scala2PreSuper = termFlag(58, "<presuper>") - - /** A macro (Scala 2.x only) */ - final val Macro = commonFlag(59, "<macro>") - - /** A method that is known to have inherited default parameters */ - final val InheritedDefaultParams = termFlag(60, "<inherited-default-param>") - - /** A method that is known to have no default parameters */ - final val NoDefaultParams = termFlag(61, "<no-default-param>") - - /** A denotation that is valid in all run-ids */ - final val Permanent = commonFlag(62, "<permanent>") - -// --------- Combined Flag Sets and Conjunctions ---------------------- - - /** Flags representing source modifiers */ - final val SourceModifierFlags = - commonFlags(Private, Protected, Abstract, Final, Inline, - Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic) - - /** Flags representing modifiers that can appear in trees */ - final val ModifierFlags = - SourceModifierFlags | Module | Param | Synthetic | Package | Local | - commonFlags(Mutable) - // | Trait is subsumed by commonFlags(Lazy) from SourceModifierFlags - - assert(ModifierFlags.isTermFlags && ModifierFlags.isTypeFlags) - - /** Flags representing access rights */ - final val AccessFlags = Private | Protected | Local - - /** Flags guaranteed to be set upon symbol creation */ - final val FromStartFlags = - AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | - Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | - CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic | - Inline | LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass - - assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags) - // TODO: Should check that FromStartFlags do not change in completion - - /** A value that's unstable unless complemented with a Stable flag */ - final val UnstableValue = Mutable | Method - - /** Flags that express the variance of a type parameter. */ - final val VarianceFlags = Covariant | Contravariant - - /** Flags that are passed from a type parameter of a class to a refinement symbol - * that sets the type parameter */ - final val RetainedTypeArgFlags = VarianceFlags | ExpandedName | Protected | Local - - /** Modules always have these flags set */ - final val ModuleCreationFlags = ModuleVal | Lazy | Final | Stable - - /** Module classes always have these flags set */ - final val ModuleClassCreationFlags = ModuleClass | Final - - /** Accessors always have these flags set */ - final val AccessorCreationFlags = Method | Accessor - - /** Pure interfaces always have these flags */ - final val PureInterfaceCreationFlags = Trait | NoInits | PureInterface - - final val NoInitsInterface = NoInits | PureInterface - - /** The flags of the self symbol */ - final val SelfSymFlags = Private | Local | Deferred - - /** The flags of a class type parameter */ - final def ClassTypeParamCreationFlags = TypeParam | Deferred | Protected | Local - - /** Flags that can apply to both a module val and a module class, except those that - * are added at creation anyway - */ - final val RetainedModuleValAndClassFlags: FlagSet = - AccessFlags | Package | Case | - Synthetic | ExpandedName | JavaDefined | JavaStatic | Artifact | - Erroneous | Lifted | MixedIn | Specialized - - /** Flags that can apply to a module val */ - final val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags | - Override | Final | Method | Implicit | Lazy | - Accessor | AbsOverride | Stable | Captured | Synchronized - - /** Flags that can apply to a module class */ - final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | - InSuperCall | ImplClass - - /** Packages and package classes always have these flags set */ - final val PackageCreationFlags = - Module | Package | Final | JavaDefined - - /** These flags are pickled */ - final val PickledFlags = flagRange(FirstFlag, FirstNotPickledFlag) - - final val AnyFlags = flagRange(FirstFlag, MaxFlag) - - /** An abstract class or a trait */ - final val AbstractOrTrait = Abstract | Trait - - /** Labeled `private` or `protected[local]` */ - final val PrivateOrLocal = Private | Local - - /** Either a module or a final class */ - final val ModuleOrFinal = ModuleClass | Final - - /** Either mutable or lazy */ - final val MutableOrLazy = Mutable | Lazy - - /** Either method or lazy */ - final val MethodOrLazy = Method | Lazy - - /** Either method or lazy or deferred */ - final val MethodOrLazyOrDeferred = Method | Lazy | Deferred - - /** Labeled `private`, `final`, or `inline` */ - final val PrivateOrFinalOrInline = Private | Final | Inline - - /** A private method */ - final val PrivateMethod = allOf(Private, Method) - - /** A private accessor */ - final val PrivateAccessor = allOf(Private, Accessor) - - /** A type parameter with synthesized name */ - final val ExpandedTypeParam = allOf(ExpandedName, TypeParam) - - /** An inline method */ - final val InlineMethod = allOf(Inline, Method) - - /** An inline parameter */ - final val InlineParam = allOf(Inline, Param) - - /** A parameter or parameter accessor */ - final val ParamOrAccessor = Param | ParamAccessor - - /** A lazy or deferred value */ - final val LazyOrDeferred = Lazy | Deferred - - /** A synthetic or private definition */ - final val SyntheticOrPrivate = Synthetic | Private - - /** A type parameter or type parameter accessor */ - final val TypeParamOrAccessor = TypeParam | TypeParamAccessor - - /** A deferred member or a parameter accessor (these don't have right hand sides) */ - final val DeferredOrParamAccessor = Deferred | ParamAccessor - - /** value that's final or inline */ - final val FinalOrInline = Final | Inline - - /** If symbol of a type alias has these flags, prefer the alias */ - final val AliasPreferred = TypeParam | BaseTypeArg | ExpandedName - - /** A covariant type parameter instance */ - final val LocalCovariant = allOf(Local, Covariant) - - /** A contravariant type parameter instance */ - final val LocalContravariant = allOf(Local, Contravariant) - - /** Has defined or inherited default parameters */ - final val HasDefaultParams = DefaultParameterized | InheritedDefaultParams - - /** Is valid forever */ - final val ValidForever = Package | Permanent | Scala2ExistentialCommon - - /** Is a default parameter in Scala 2*/ - final val DefaultParameter = allOf(Param, DefaultParameterized) - - /** A trait that does not need to be initialized */ - final val NoInitsTrait = allOf(Trait, NoInits) - - /** A Java interface, potentially with default methods */ - final val JavaTrait = allOf(JavaDefined, Trait, NoInits) - - /** A Java interface */ // TODO when unpickling, reconstitute from context - final val JavaInterface = allOf(JavaDefined, Trait) - - /** A Java companion object */ - final val JavaModule = allOf(JavaDefined, Module) - - /** A Java companion object */ - final val JavaProtected = allOf(JavaDefined, Protected) - - /** Labeled private[this] */ - final val PrivateLocal = allOf(Private, Local) - - /** A private[this] parameter accessor */ - final val PrivateLocalParamAccessor = allOf(Private, Local, ParamAccessor) - - /** A parameter forwarder */ - final val ParamForwarder = allOf(Method, Stable, ParamAccessor) - - /** A private[this] parameter */ - final val PrivateLocalParam = allOf(Private, Local, Param) - - /** A private parameter accessor */ - final val PrivateParamAccessor = allOf(Private, ParamAccessor) - - /** A type parameter introduced with [type ... ] */ - final val NamedTypeParam = allOf(TypeParam, ParamAccessor) - - /** A local parameter */ - final val ParamAndLocal = allOf(Param, Local) - - /** Labeled protected[this] */ - final val ProtectedLocal = allOf(Protected, Local) - - /** Java symbol which is `protected` and `static` */ - final val StaticProtected = allOf(JavaDefined, Protected, JavaStatic) - - final val AbstractFinal = allOf(Abstract, Final) - final val AbstractSealed = allOf(Abstract, Sealed) - final val SyntheticArtifact = allOf(Synthetic, Artifact) - final val SyntheticModule = allOf(Synthetic, Module) - final val SyntheticTermParam = allOf(Synthetic, TermParam) - final val SyntheticTypeParam = allOf(Synthetic, TypeParam) - final val SyntheticCase = allOf(Synthetic, Case) - final val AbstractAndOverride = allOf(Abstract, Override) - final val Scala2Trait = allOf(Scala2x, Trait) - - implicit def conjToFlagSet(conj: FlagConjunction): FlagSet = - FlagSet(conj.bits) -} diff --git a/src/dotty/tools/dotc/core/Hashable.scala b/src/dotty/tools/dotc/core/Hashable.scala deleted file mode 100644 index e4510c53e..000000000 --- a/src/dotty/tools/dotc/core/Hashable.scala +++ /dev/null @@ -1,103 +0,0 @@ -package dotty.tools.dotc -package core - -import Types._ -import scala.util.hashing.{ MurmurHash3 => hashing } - -object Hashable { - - /** A hash value indicating that the underlying type is not - * cached in uniques. - */ - final val NotCached = 0 - - /** An alternative value returned from `hash` if the - * computed hashCode would be `NotCached`. - */ - private[core] final val NotCachedAlt = Int.MinValue - - /** A value that indicates that the hash code is unknown - */ - private[core] final val HashUnknown = 1234 - - /** An alternative value if computeHash would otherwise yield HashUnknown - */ - private[core] final val HashUnknownAlt = 4321 -} - -trait Hashable { - import Hashable._ - - protected def hashSeed: Int = getClass.hashCode - - protected final def finishHash(hashCode: Int, arity: Int): Int = - avoidSpecialHashes(hashing.finalizeHash(hashCode, arity)) - - final def identityHash = avoidSpecialHashes(System.identityHashCode(this)) - - protected def finishHash(seed: Int, arity: Int, tp: Type): Int = { - val elemHash = tp.hash - if (elemHash == NotCached) return NotCached - finishHash(hashing.mix(seed, elemHash), arity + 1) - } - - protected def finishHash(seed: Int, arity: Int, tp1: Type, tp2: Type): Int = { - val elemHash = tp1.hash - if (elemHash == NotCached) return NotCached - finishHash(hashing.mix(seed, elemHash), arity + 1, tp2) - } - - protected def finishHash(seed: Int, arity: Int, tps: List[Type]): Int = { - var h = seed - var xs = tps - var len = arity - while (xs.nonEmpty) { - val elemHash = xs.head.hash - if (elemHash == NotCached) return NotCached - h = hashing.mix(h, elemHash) - xs = xs.tail - len += 1 - } - finishHash(h, len) - } - - protected def finishHash(seed: Int, arity: Int, tp: Type, tps: List[Type]): Int = { - val elemHash = tp.hash - if (elemHash == NotCached) return NotCached - finishHash(hashing.mix(seed, elemHash), arity + 1, tps) - } - - protected final def doHash(x: Any): Int = - finishHash(hashing.mix(hashSeed, x.hashCode), 1) - - protected final def doHash(tp: Type): Int = - finishHash(hashSeed, 0, tp) - - protected final def doHash(x1: Any, tp2: Type): Int = - finishHash(hashing.mix(hashSeed, x1.hashCode), 1, tp2) - - protected final def doHash(tp1: Type, tp2: Type): Int = - finishHash(hashSeed, 0, tp1, tp2) - - protected final def doHash(x1: Any, tp2: Type, tp3: Type): Int = - finishHash(hashing.mix(hashSeed, x1.hashCode), 1, tp2, tp3) - - protected final def doHash(tp1: Type, tps2: List[Type]): Int = - finishHash(hashSeed, 0, tp1, tps2) - - protected final def doHash(x1: Any, tp2: Type, tps3: List[Type]): Int = - finishHash(hashing.mix(hashSeed, x1.hashCode), 1, tp2, tps3) - - - protected final def doHash(x1: Int, x2: Int): Int = - finishHash(hashing.mix(hashing.mix(hashSeed, x1), x2), 1) - - protected final def addDelta(elemHash: Int, delta: Int) = - if (elemHash == NotCached) NotCached - else avoidSpecialHashes(elemHash + delta) - - private def avoidSpecialHashes(h: Int) = - if (h == NotCached) NotCachedAlt - else if (h == HashUnknown) HashUnknownAlt - else h -} diff --git a/src/dotty/tools/dotc/core/Mode.scala b/src/dotty/tools/dotc/core/Mode.scala deleted file mode 100644 index 406a84af6..000000000 --- a/src/dotty/tools/dotc/core/Mode.scala +++ /dev/null @@ -1,89 +0,0 @@ -package dotty.tools.dotc.core - -/** A collection of mode bits that are part of a context */ -case class Mode(val bits: Int) extends AnyVal { - import Mode._ - def | (that: Mode) = Mode(bits | that.bits) - def & (that: Mode) = Mode(bits & that.bits) - def &~ (that: Mode) = Mode(bits & ~that.bits) - def is (that: Mode) = (bits & that.bits) == that.bits - - def isExpr = (this & PatternOrType) == None - - override def toString = - (0 until 31).filter(i => (bits & (1 << i)) != 0).map(modeName).mkString("Mode(", ",", ")") -} - -object Mode { - val None = Mode(0) - - private val modeName = new Array[String](32) - - def newMode(bit: Int, name: String): Mode = { - modeName(bit) = name - Mode(1 << bit) - } - - val Pattern = newMode(0, "Pattern") - val Type = newMode(1, "Type") - - val ImplicitsEnabled = newMode(2, "ImplicitsEnabled") - val InferringReturnType = newMode(3, "InferringReturnType") - - /** This mode bit is set if we collect information without reference to a valid - * context with typerstate and constraint. This is typically done when we - * cache the eligibility of implicits. Caching needs to be done across different constraints. - * Therefore, if TypevarsMissContext is set, subtyping becomes looser, and assumes - * that PolyParams can be sub- and supertypes of anything. See TypeComparer. - */ - val TypevarsMissContext = newMode(4, "TypevarsMissContext") - val CheckCyclic = newMode(5, "CheckCyclic") - - val InSuperCall = newMode(6, "InSuperCall") - - /** Allow GADTFlexType labelled types to have their bounds adjusted */ - val GADTflexible = newMode(8, "GADTflexible") - - /** Allow dependent functions. This is currently necessary for unpickling, because - * some dependent functions are passed through from the front end(s?), even though they - * are technically speaking illegal. - */ - val AllowDependentFunctions = newMode(9, "AllowDependentFunctions") - - /** We are currently printing something: avoid to produce more logs about - * the printing - */ - val Printing = newMode(10, "Printing") - - /** We are currently typechecking an ident to determine whether some implicit - * is shadowed - don't do any other shadowing tests. - */ - val ImplicitShadowing = newMode(11, "ImplicitShadowing") - - /** We are currently in a `viewExists` check. In that case, ambiguous - * implicits checks are disabled and we succeed with the first implicit - * found. - */ - val ImplicitExploration = newMode(12, "ImplicitExploration") - - /** We are currently unpickling Scala2 info */ - val Scala2Unpickling = newMode(13, "Scala2Unpickling") - - /** Use Scala2 scheme for overloading and implicit resolution */ - val OldOverloadingResolution = newMode(14, "OldOverloadingResolution") - - /** Allow hk applications of type lambdas to wildcard arguments; - * used for checking that such applications do not normally arise - */ - val AllowLambdaWildcardApply = newMode(15, "AllowHKApplyToWildcards") - - /** Read original positions when unpickling from TASTY */ - val ReadPositions = newMode(16, "ReadPositions") - - val PatternOrType = Pattern | Type - - /** We are elaborating the fully qualified name of a package clause. - * In this case, identifiers should never be imported. - */ - val InPackageClauseName = newMode(17, "InPackageClauseName") -} diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala deleted file mode 100644 index 4c7f5b0a9..000000000 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ /dev/null @@ -1,432 +0,0 @@ -package dotty.tools.dotc -package core - -import java.security.MessageDigest -import scala.annotation.switch -import scala.io.Codec -import Names._, StdNames._, Contexts._, Symbols._, Flags._ -import Decorators.StringDecorator -import util.{Chars, NameTransformer} -import Chars.isOperatorPart - -object NameOps { - - final object compactify { - lazy val md5 = MessageDigest.getInstance("MD5") - - /** COMPACTIFY - * - * The hashed name has the form (prefix + marker + md5 + marker + suffix), where - * - prefix/suffix.length = MaxNameLength / 4 - * - md5.length = 32 - * - * We obtain the formula: - * - * FileNameLength = 2*(MaxNameLength / 4) + 2.marker.length + 32 + 6 - * - * (+6 for ".class"). MaxNameLength can therefore be computed as follows: - */ - def apply(s: String)(implicit ctx: Context): String = { - val marker = "$$$$" - val limit: Int = ctx.settings.maxClassfileName.value - val MaxNameLength = (limit - 6) min 2 * (limit - 6 - 2 * marker.length - 32) - - def toMD5(s: String, edge: Int): String = { - val prefix = s take edge - val suffix = s takeRight edge - - val cs = s.toArray - val bytes = Codec toUTF8 cs - md5 update bytes - val md5chars = (md5.digest() map (b => (b & 0xFF).toHexString)).mkString - - prefix + marker + md5chars + marker + suffix - } - - if (s.length <= MaxNameLength) s else toMD5(s, MaxNameLength / 4) - } - } - - class PrefixNameExtractor(pre: TermName) { - def apply(name: TermName): TermName = pre ++ name - def unapply(name: TermName): Option[TermName] = - if (name startsWith pre) Some(name.drop(pre.length).asTermName) else None - } - - object SuperAccessorName extends PrefixNameExtractor(nme.SUPER_PREFIX) - object InitializerName extends PrefixNameExtractor(nme.INITIALIZER_PREFIX) - - implicit class NameDecorator[N <: Name](val name: N) extends AnyVal { - import nme._ - - def likeTyped(n: PreName): N = - (if (name.isTermName) n.toTermName else n.toTypeName).asInstanceOf[N] - - def isConstructorName = name == CONSTRUCTOR || name == TRAIT_CONSTRUCTOR - def isStaticConstructorName = name == STATIC_CONSTRUCTOR - def isExceptionResultName = name startsWith EXCEPTION_RESULT_PREFIX - def isImplClassName = name endsWith IMPL_CLASS_SUFFIX - def isLocalDummyName = name startsWith LOCALDUMMY_PREFIX - def isLoopHeaderLabel = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX) - def isProtectedAccessorName = name startsWith PROTECTED_PREFIX - def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER - def isTraitSetterName = name containsSlice TRAIT_SETTER_SEPARATOR - def isSetterName = name endsWith SETTER_SUFFIX - def isSingletonName = name endsWith SINGLETON_SUFFIX - def isModuleClassName = name endsWith MODULE_SUFFIX - def isAvoidClashName = name endsWith AVOID_CLASH_SUFFIX - def isImportName = name startsWith IMPORT - def isFieldName = name endsWith LOCAL_SUFFIX - def isShadowedName = name.length > 0 && name.head == '(' && name.startsWith(nme.SHADOWED) - def isDefaultGetterName = name.isTermName && name.asTermName.defaultGetterIndex >= 0 - def isScala2LocalSuffix = name.endsWith(" ") - def isModuleVarName(name: Name): Boolean = - name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX - def isSelectorName = name.startsWith(" ") && name.tail.forall(_.isDigit) - def isLazyLocal = name.endsWith(nme.LAZY_LOCAL) - def isOuterSelect = name.endsWith(nme.OUTER_SELECT) - def isInlineAccessor = name.startsWith(nme.INLINE_ACCESSOR_PREFIX) - - /** Is name a variable name? */ - def isVariableName: Boolean = name.length > 0 && { - val first = name.head - (((first.isLower && first.isLetter) || first == '_') - && (name != false_) - && (name != true_) - && (name != null_)) - } - - def isOpAssignmentName: Boolean = name match { - case raw.NE | raw.LE | raw.GE | EMPTY => - false - case _ => - name.length > 0 && name.last == '=' && name.head != '=' && isOperatorPart(name.head) - } - - /** If the name ends with $nn where nn are - * all digits, strip the $ and the digits. - * Otherwise return the argument. - */ - def stripAnonNumberSuffix: Name = { - var pos = name.length - while (pos > 0 && name(pos - 1).isDigit) - pos -= 1 - - if (pos > 0 && pos < name.length && name(pos - 1) == '$') - name take (pos - 1) - else - name - } - - /** Convert this module name to corresponding module class name */ - def moduleClassName: TypeName = (name ++ tpnme.MODULE_SUFFIX).toTypeName - - /** Convert this module class name to corresponding source module name */ - def sourceModuleName: TermName = stripModuleClassSuffix.toTermName - - /** If name ends in module class suffix, drop it */ - def stripModuleClassSuffix: Name = - if (isModuleClassName) name dropRight MODULE_SUFFIX.length else name - - /** Append a suffix so that this name does not clash with another name in the same scope */ - def avoidClashName: TermName = (name ++ AVOID_CLASH_SUFFIX).toTermName - - /** If name ends in "avoid clash" suffix, drop it */ - def stripAvoidClashSuffix: Name = - if (isAvoidClashName) name dropRight AVOID_CLASH_SUFFIX.length else name - - /** If flags is a ModuleClass but not a Package, add module class suffix */ - def adjustIfModuleClass(flags: Flags.FlagSet): N = { - if (flags is (ModuleClass, butNot = Package)) name.asTypeName.moduleClassName - else stripAvoidClashSuffix - }.asInstanceOf[N] - - /** The superaccessor for method with given name */ - def superName: TermName = (nme.SUPER_PREFIX ++ name).toTermName - - /** The expanded name of `name` relative to given class `base`. - */ - def expandedName(base: Symbol, separator: Name)(implicit ctx: Context): N = - expandedName(if (base is Flags.ExpandedName) base.name else base.fullNameSeparated("$"), separator) - - def expandedName(base: Symbol)(implicit ctx: Context): N = expandedName(base, nme.EXPAND_SEPARATOR) - - /** The expanded name of `name` relative to `basename` with given `separator` - */ - def expandedName(prefix: Name, separator: Name = nme.EXPAND_SEPARATOR): N = - name.fromName(prefix ++ separator ++ name).asInstanceOf[N] - - def expandedName(prefix: Name): N = expandedName(prefix, nme.EXPAND_SEPARATOR) - - /** Revert the expanded name. Note: This currently gives incorrect results - * if the normal name contains `nme.EXPAND_SEPARATOR`, i.e. two consecutive '$' - * signs. This can happen for instance if a super accessor is paired with - * an encoded name, e.g. super$$plus$eq. See #765. - */ - def unexpandedName: N = { - var idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR) - - // Hack to make super accessors from traits work. They would otherwise fail because of #765 - // TODO: drop this once we have more robust name handling - if (idx > FalseSuperLength && name.slice(idx - FalseSuperLength, idx) == FalseSuper) - idx -= FalseSuper.length - - if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N] - } - - def expandedPrefix: N = { - val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR) - assert(idx >= 0) - name.take(idx).asInstanceOf[N] - } - - def shadowedName: N = likeTyped(nme.SHADOWED ++ name) - - def revertShadowed: N = likeTyped(name.drop(nme.SHADOWED.length)) - - def implClassName: N = likeTyped(name ++ tpnme.IMPL_CLASS_SUFFIX) - - def errorName: N = likeTyped(name ++ nme.ERROR) - - def freshened(implicit ctx: Context): N = - likeTyped( - if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName - else likeTyped(ctx.freshName(name ++ NameTransformer.NAME_JOIN_STRING))) - - /** Translate a name into a list of simple TypeNames and TermNames. - * In all segments before the last, type/term is determined by whether - * the following separator char is '.' or '#'. The last segment - * is of the same type as the original name. - * - * Examples: - * - * package foo { - * object Lorax { object Wog ; class Wog } - * class Lorax { object Zax ; class Zax } - * } - * - * f("foo.Lorax".toTermName) == List("foo": Term, "Lorax": Term) // object Lorax - * f("foo.Lorax".toTypeName) == List("foo": Term, "Lorax": Type) // class Lorax - * f("Lorax.Wog".toTermName) == List("Lorax": Term, "Wog": Term) // object Wog - * f("Lorax.Wog".toTypeName) == List("Lorax": Term, "Wog": Type) // class Wog - * f("Lorax#Zax".toTermName) == List("Lorax": Type, "Zax": Term) // object Zax - * f("Lorax#Zax".toTypeName) == List("Lorax": Type, "Zax": Type) // class Zax - * - * Note that in actual scala syntax you cannot refer to object Zax without an - * instance of Lorax, so Lorax#Zax could only mean the type. One might think - * that Lorax#Zax.type would work, but this is not accepted by the parser. - * For the purposes of referencing that object, the syntax is allowed. - */ - def segments: List[Name] = { - def mkName(name: Name, follow: Char): Name = - if (follow == '.') name.toTermName else name.toTypeName - - name.indexWhere(ch => ch == '.' || ch == '#') match { - case -1 => - if (name.isEmpty) scala.Nil else name :: scala.Nil - case idx => - mkName(name take idx, name(idx)) :: (name drop (idx + 1)).segments - } - } - - /** The name of the generic runtime operation corresponding to an array operation */ - def genericArrayOp: TermName = name match { - case nme.apply => nme.array_apply - case nme.length => nme.array_length - case nme.update => nme.array_update - case nme.clone_ => nme.array_clone - } - - /** The name of the primitive runtime operation corresponding to an array operation */ - def primitiveArrayOp: TermName = name match { - case nme.apply => nme.primitive.arrayApply - case nme.length => nme.primitive.arrayLength - case nme.update => nme.primitive.arrayUpdate - case nme.clone_ => nme.clone_ - } - - def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = { - - def typeToTag(tp: Types.Type): Name = { - tp.classSymbol match { - case t if t eq defn.IntClass => nme.specializedTypeNames.Int - case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean - case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte - case t if t eq defn.LongClass => nme.specializedTypeNames.Long - case t if t eq defn.ShortClass => nme.specializedTypeNames.Short - case t if t eq defn.FloatClass => nme.specializedTypeNames.Float - case t if t eq defn.UnitClass => nme.specializedTypeNames.Void - case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double - case t if t eq defn.CharClass => nme.specializedTypeNames.Char - case _ => nme.specializedTypeNames.Object - } - } - - val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1)) - val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) - - name.fromName(name ++ nme.specializedTypeNames.prefix ++ - methodTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.separator ++ - classTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix) - } - - /** If name length exceeds allowable limit, replace part of it by hash */ - def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString)) - } - - // needed??? - private val Boxed = Map[TypeName, TypeName]( - tpnme.Boolean -> jtpnme.BoxedBoolean, - tpnme.Byte -> jtpnme.BoxedByte, - tpnme.Char -> jtpnme.BoxedCharacter, - tpnme.Short -> jtpnme.BoxedShort, - tpnme.Int -> jtpnme.BoxedInteger, - tpnme.Long -> jtpnme.BoxedLong, - tpnme.Float -> jtpnme.BoxedFloat, - tpnme.Double -> jtpnme.BoxedDouble) - - implicit class TermNameDecorator(val name: TermName) extends AnyVal { - import nme._ - - def setterName: TermName = - if (name.isFieldName) name.fieldToGetter.setterName - else name ++ SETTER_SUFFIX - - def getterName: TermName = - if (name.isFieldName) fieldToGetter - else setterToGetter - - def fieldName: TermName = - if (name.isSetterName) { - if (name.isTraitSetterName) { - // has form <$-separated-trait-name>$_setter_$ `name`_$eq - val start = name.indexOfSlice(TRAIT_SETTER_SEPARATOR) + TRAIT_SETTER_SEPARATOR.length - val end = name.indexOfSlice(SETTER_SUFFIX) - name.slice(start, end) ++ LOCAL_SUFFIX - } else getterName.fieldName - } - else name ++ LOCAL_SUFFIX - - private def setterToGetter: TermName = { - assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") - name.take(name.length - SETTER_SUFFIX.length).asTermName - } - - def fieldToGetter: TermName = { - assert(name.isFieldName) - name.take(name.length - LOCAL_SUFFIX.length).asTermName - } - - /** Nominally, name$default$N, encoded for <init> - * @param Post the parameters position. - * @note Default getter name suffixes start at 1, so `pos` has to be adjusted by +1 - */ - def defaultGetterName(pos: Int): TermName = { - val prefix = if (name.isConstructorName) DEFAULT_GETTER_INIT else name - prefix ++ DEFAULT_GETTER ++ (pos + 1).toString - } - - /** Nominally, name from name$default$N, CONSTRUCTOR for <init> */ - def defaultGetterToMethod: TermName = { - val p = name.indexOfSlice(DEFAULT_GETTER) - if (p >= 0) { - val q = name.take(p).asTermName - // i.e., if (q.decoded == CONSTRUCTOR.toString) CONSTRUCTOR else q - if (q == DEFAULT_GETTER_INIT) CONSTRUCTOR else q - } else name - } - - /** If this is a default getter, its index (starting from 0), else -1 */ - def defaultGetterIndex: Int = { - var i = name.length - while (i > 0 && name(i - 1).isDigit) i -= 1 - if (i > 0 && i < name.length && name.take(i).endsWith(DEFAULT_GETTER)) - name.drop(i).toString.toInt - 1 - else - -1 - } - - def stripScala2LocalSuffix: TermName = - if (name.isScala2LocalSuffix) name.init.asTermName else name - - /** The name of an accessor for protected symbols. */ - def protectedAccessorName: TermName = - PROTECTED_PREFIX ++ name.unexpandedName - - /** The name of a setter for protected symbols. Used for inherited Java fields. */ - def protectedSetterName: TermName = - PROTECTED_SET_PREFIX ++ name.unexpandedName - - def moduleVarName: TermName = - name ++ MODULE_VAR_SUFFIX - - /** The name unary_x for a prefix operator x */ - def toUnaryName: TermName = name match { - case raw.MINUS => UNARY_- - case raw.PLUS => UNARY_+ - case raw.TILDE => UNARY_~ - case raw.BANG => UNARY_! - case _ => name - } - - /** The name of a method which stands in for a primitive operation - * during structural type dispatch. - */ - def primitiveInfixMethodName: TermName = name match { - case OR => takeOr - case XOR => takeXor - case AND => takeAnd - case EQ => testEqual - case NE => testNotEqual - case ADD => add - case SUB => subtract - case MUL => multiply - case DIV => divide - case MOD => takeModulo - case LSL => shiftSignedLeft - case LSR => shiftLogicalRight - case ASR => shiftSignedRight - case LT => testLessThan - case LE => testLessOrEqualThan - case GE => testGreaterOrEqualThan - case GT => testGreaterThan - case ZOR => takeConditionalOr - case ZAND => takeConditionalAnd - case _ => NO_NAME - } - - /** Postfix/prefix, really. - */ - def primitivePostfixMethodName: TermName = name match { - case UNARY_! => takeNot - case UNARY_+ => positive - case UNARY_- => negate - case UNARY_~ => complement - case `toByte` => toByte - case `toShort` => toShort - case `toChar` => toCharacter - case `toInt` => toInteger - case `toLong` => toLong - case `toFloat` => toFloat - case `toDouble` => toDouble - case _ => NO_NAME - } - - def primitiveMethodName: TermName = - primitiveInfixMethodName match { - case NO_NAME => primitivePostfixMethodName - case name => name - } - - def lazyLocalName = name ++ nme.LAZY_LOCAL - def nonLazyName = { - assert(name.isLazyLocal) - name.dropRight(nme.LAZY_LOCAL.length) - } - - def inlineAccessorName = nme.INLINE_ACCESSOR_PREFIX ++ name ++ "$" - } - - private final val FalseSuper = "$$super".toTermName - private val FalseSuperLength = FalseSuper.length -} diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala deleted file mode 100644 index 11f0b55a8..000000000 --- a/src/dotty/tools/dotc/core/Names.scala +++ /dev/null @@ -1,372 +0,0 @@ -package dotty.tools -package dotc -package core - -import scala.io.Codec -import util.NameTransformer -import printing.{Showable, Texts, Printer} -import Texts.Text -import Decorators._ -import Contexts.Context -import collection.IndexedSeqOptimized -import collection.generic.CanBuildFrom -import collection.mutable.{ Builder, StringBuilder } -import collection.immutable.WrappedString -import collection.generic.CanBuildFrom -import util.DotClass -//import annotation.volatile - -object Names { - - /** A common class for things that can be turned into names. - * Instances are both names and strings, the latter via a decorator. - */ - trait PreName extends Any with Showable { - def toTypeName: TypeName - def toTermName: TermName - } - - implicit def eqName: Eq[Name, Name] = Eq - - /** A name is essentially a string, with three differences - * 1. Names belong in one of two name spaces: they are type names or term names. - * Term names have a sub-category of "local" field names. - * The same string can correspond a name in each of the three namespaces. - * 2. Names are hash-consed. Two names - * representing the same string in the same universe are always reference identical. - * 3. Names are intended to be encoded strings. @see dotc.util.NameTransformer. - * The encoding will be applied when converting a string to a name. - */ - abstract class Name extends DotClass - with PreName - with collection.immutable.Seq[Char] - with IndexedSeqOptimized[Char, Name] { - - /** A type for names of the same kind as this name */ - type ThisName <: Name - - /** The start index in the character array */ - val start: Int - - /** The length of the names */ - override val length: Int - - /** Is this name a type name? */ - def isTypeName: Boolean - - /** Is this name a term name? */ - def isTermName: Boolean - - /** This name converted to a type name */ - def toTypeName: TypeName - - /** This name converted to a term name */ - def toTermName: TermName - - /** This name downcasted to a type name */ - def asTypeName: TypeName - - /** This name downcasted to a term name */ - def asTermName: TermName - - /** Create a new name of same kind as this one, in the given - * basis, with `len` characters taken from `cs` starting at `offset`. - */ - def fromChars(cs: Array[Char], offset: Int, len: Int): ThisName - - /** Create new name of same kind as this name and with same - * characters as given `name`. - */ - def fromName(name: Name): ThisName = fromChars(chrs, name.start, name.length) - - /** Create new name of same kind as this name with characters from - * the given string - */ - def fromString(str: String): ThisName = { - val cs = str.toCharArray - fromChars(cs, 0, cs.length) - } - - override def toString = - if (length == 0) "" else new String(chrs, start, length) - - def toText(printer: Printer): Text = printer.toText(this) - - /** Write to UTF8 representation of this name to given character array. - * Start copying to index `to`. Return index of next free byte in array. - * Array must have enough remaining space for all bytes - * (i.e. maximally 3*length bytes). - */ - final def copyUTF8(bs: Array[Byte], offset: Int): Int = { - val bytes = Codec.toUTF8(chrs, start, length) - scala.compat.Platform.arraycopy(bytes, 0, bs, offset, bytes.length) - offset + bytes.length - } - - /** Replace \$op_name's by corresponding operator symbols. */ - def decode: Name = - if (contains('$')) fromString(NameTransformer.decode(toString)) - else this - - /** Replace operator symbols by corresponding \$op_name's. */ - def encode: Name = - if (dontEncode(toTermName)) this else NameTransformer.encode(this) - - /** A more efficient version of concatenation */ - def ++ (other: Name): ThisName = ++ (other.toString) - - def ++ (other: String): ThisName = { - val s = toString + other - fromChars(s.toCharArray, 0, s.length) - } - - def replace(from: Char, to: Char): ThisName = { - val cs = new Array[Char](length) - Array.copy(chrs, start, cs, 0, length) - for (i <- 0 until length) { - if (cs(i) == from) cs(i) = to - } - fromChars(cs, 0, length) - } - - def contains(ch: Char): Boolean = { - var i = 0 - while (i < length && chrs(start + i) != ch) i += 1 - i < length - } - - def firstChar = chrs(start) - - // ----- Collections integration ------------------------------------- - - override protected[this] def thisCollection: WrappedString = new WrappedString(repr.toString) - override protected[this] def toCollection(repr: Name): WrappedString = new WrappedString(repr.toString) - - override protected[this] def newBuilder: Builder[Char, Name] = unsupported("newBuilder") - - override def apply(index: Int): Char = chrs(start + index) - - override def slice(from: Int, until: Int): ThisName = - fromChars(chrs, start + from, until - from) - - override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] - - override def seq = toCollection(this) - } - - class TermName(val start: Int, val length: Int, @sharable private[Names] var next: TermName) extends Name { - // `next` is @sharable because it is only modified in the synchronized block of termName. - type ThisName = TermName - def isTypeName = false - def isTermName = true - - @sharable // because it is only modified in the synchronized block of toTypeName. - @volatile private[this] var _typeName: TypeName = null - - def toTypeName: TypeName = { - if (_typeName == null) - synchronized { - if (_typeName == null) - _typeName = new TypeName(start, length, this) - } - _typeName - } - def toTermName = this - def asTypeName = throw new ClassCastException(this + " is not a type name") - def asTermName = this - - override def hashCode: Int = start - - override protected[this] def newBuilder: Builder[Char, Name] = termNameBuilder - - def fromChars(cs: Array[Char], offset: Int, len: Int): TermName = termName(cs, offset, len) - } - - class TypeName(val start: Int, val length: Int, val toTermName: TermName) extends Name { - type ThisName = TypeName - def isTypeName = true - def isTermName = false - def toTypeName = this - def asTypeName = this - def asTermName = throw new ClassCastException(this + " is not a term name") - - override def hashCode: Int = -start - - override protected[this] def newBuilder: Builder[Char, Name] = - termNameBuilder.mapResult(_.toTypeName) - - def fromChars(cs: Array[Char], offset: Int, len: Int): TypeName = typeName(cs, offset, len) - } - - // Nametable - - private final val InitialHashSize = 0x8000 - private final val InitialNameSize = 0x20000 - private final val fillFactor = 0.7 - - /** Memory to store all names sequentially. */ - @sharable // because it's only mutated in synchronized block of termName - private[dotty] var chrs: Array[Char] = new Array[Char](InitialNameSize) - - /** The number of characters filled. */ - @sharable // because it's only mutated in synchronized block of termName - private var nc = 0 - - /** Hashtable for finding term names quickly. */ - @sharable // because it's only mutated in synchronized block of termName - private var table = new Array[TermName](InitialHashSize) - - /** The number of defined names. */ - @sharable // because it's only mutated in synchronized block of termName - private var size = 1 - - /** The hash of a name made of from characters cs[offset..offset+len-1]. */ - private def hashValue(cs: Array[Char], offset: Int, len: Int): Int = - if (len > 0) - (len * (41 * 41 * 41) + - cs(offset) * (41 * 41) + - cs(offset + len - 1) * 41 + - cs(offset + (len >> 1))) - else 0 - - /** Is (the ASCII representation of) name at given index equal to - * cs[offset..offset+len-1]? - */ - private def equals(index: Int, cs: Array[Char], offset: Int, len: Int): Boolean = { - var i = 0 - while ((i < len) && (chrs(index + i) == cs(offset + i))) - i += 1 - i == len - } - - /** Create a term name from the characters in cs[offset..offset+len-1]. - * Assume they are already encoded. - */ - def termName(cs: Array[Char], offset: Int, len: Int): TermName = synchronized { - util.Stats.record("termName") - val h = hashValue(cs, offset, len) & (table.size - 1) - - /** Make sure the capacity of the character array is at least `n` */ - def ensureCapacity(n: Int) = - if (n > chrs.length) { - val newchrs = new Array[Char](chrs.length * 2) - chrs.copyToArray(newchrs) - chrs = newchrs - } - - /** Enter characters into chrs array. */ - def enterChars(): Unit = { - ensureCapacity(nc + len) - var i = 0 - while (i < len) { - chrs(nc + i) = cs(offset + i) - i += 1 - } - nc += len - } - - /** Rehash chain of names */ - def rehash(name: TermName): Unit = - if (name != null) { - val oldNext = name.next - val h = hashValue(chrs, name.start, name.length) & (table.size - 1) - name.next = table(h) - table(h) = name - rehash(oldNext) - } - - /** Make sure the hash table is large enough for the given load factor */ - def incTableSize() = { - size += 1 - if (size.toDouble / table.size > fillFactor) { - val oldTable = table - table = new Array[TermName](table.size * 2) - for (i <- 0 until oldTable.size) rehash(oldTable(i)) - } - } - - val next = table(h) - var name = next - while (name ne null) { - if (name.length == len && equals(name.start, cs, offset, len)) - return name - name = name.next - } - name = new TermName(nc, len, next) - enterChars() - table(h) = name - incTableSize() - name - } - - /** Create a type name from the characters in cs[offset..offset+len-1]. - * Assume they are already encoded. - */ - def typeName(cs: Array[Char], offset: Int, len: Int): TypeName = - termName(cs, offset, len).toTypeName - - /** Create a term name from the UTF8 encoded bytes in bs[offset..offset+len-1]. - * Assume they are already encoded. - */ - def termName(bs: Array[Byte], offset: Int, len: Int): TermName = { - val chars = Codec.fromUTF8(bs, offset, len) - termName(chars, 0, chars.length) - } - - /** Create a type name from the UTF8 encoded bytes in bs[offset..offset+len-1]. - * Assume they are already encoded. - */ - def typeName(bs: Array[Byte], offset: Int, len: Int): TypeName = - termName(bs, offset, len).toTypeName - - /** Create a term name from a string, without encoding operators */ - def termName(s: String): TermName = termName(s.toCharArray, 0, s.length) - - /** Create a type name from a string, without encoding operators */ - def typeName(s: String): TypeName = typeName(s.toCharArray, 0, s.length) - - /** The term name represented by the empty string */ - val EmptyTermName = new TermName(-1, 0, null) - - table(0) = EmptyTermName - - /** The type name represented by the empty string */ - val EmptyTypeName = EmptyTermName.toTypeName - - // can't move CONSTRUCTOR/EMPTY_PACKAGE to `nme` because of bootstrap failures in `encode`. - val CONSTRUCTOR = termName("<init>") - val STATIC_CONSTRUCTOR = termName("<clinit>") - val EMPTY_PACKAGE = termName("<empty>") - - val dontEncode = Set(CONSTRUCTOR, EMPTY_PACKAGE) - - def termNameBuilder: Builder[Char, TermName] = - StringBuilder.newBuilder.mapResult(termName) - - implicit val nameCanBuildFrom: CanBuildFrom[Name, Char, Name] = new CanBuildFrom[Name, Char, Name] { - def apply(from: Name): Builder[Char, Name] = - StringBuilder.newBuilder.mapResult(s => from.fromChars(s.toCharArray, 0, s.length)) - def apply(): Builder[Char, Name] = termNameBuilder - } - - implicit val NameOrdering: Ordering[Name] = new Ordering[Name] { - def compare(x: Name, y: Name): Int = { - if (x.isTermName && y.isTypeName) 1 - else if (x.isTypeName && y.isTermName) -1 - else if (x eq y) 0 - else { - val until = x.length min y.length - var i = 0 - - while (i < until && x(i) == y(i)) i = i + 1 - - if (i < until) { - if (x(i) < y(i)) -1 - else /*(x(i) > y(i))*/ 1 - } else { - x.length - y.length - } - } - } - } -} diff --git a/src/dotty/tools/dotc/core/OrderingConstraint.scala b/src/dotty/tools/dotc/core/OrderingConstraint.scala deleted file mode 100644 index 72c7a8e51..000000000 --- a/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ /dev/null @@ -1,636 +0,0 @@ -package dotty.tools -package dotc -package core - -import Types._, Contexts._, Symbols._, Decorators._ -import util.SimpleMap -import collection.mutable -import printing.{Printer, Showable} -import printing.Texts._ -import config.Config -import collection.immutable.BitSet -import reflect.ClassTag -import annotation.tailrec - -object OrderingConstraint { - - type ArrayValuedMap[T] = SimpleMap[PolyType, Array[T]] - - /** The type of `OrderingConstraint#boundsMap` */ - type ParamBounds = ArrayValuedMap[Type] - - /** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */ - type ParamOrdering = ArrayValuedMap[List[PolyParam]] - - /** A new constraint with given maps */ - private def newConstraint(boundsMap: ParamBounds, lowerMap: ParamOrdering, upperMap: ParamOrdering)(implicit ctx: Context) : OrderingConstraint = { - val result = new OrderingConstraint(boundsMap, lowerMap, upperMap) - if (Config.checkConstraintsNonCyclic) result.checkNonCyclic() - ctx.runInfo.recordConstraintSize(result, result.boundsMap.size) - result - } - - /** A lens for updating a single entry array in one of the three constraint maps */ - abstract class ConstraintLens[T <: AnyRef: ClassTag] { - def entries(c: OrderingConstraint, poly: PolyType): Array[T] - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[T])(implicit ctx: Context): OrderingConstraint - def initial: T - - def apply(c: OrderingConstraint, poly: PolyType, idx: Int) = { - val es = entries(c, poly) - if (es == null) initial else es(idx) - } - - /** The `current` constraint but with the entry for `param` updated to `entry`. - * `current` is used linearly. If it is different from `prev` it is - * known to be dead after the call. Hence it is OK to update destructively - * parts of `current` which are not shared by `prev`. - */ - def update(prev: OrderingConstraint, current: OrderingConstraint, - poly: PolyType, idx: Int, entry: T)(implicit ctx: Context): OrderingConstraint = { - var es = entries(current, poly) - if (es != null && (es(idx) eq entry)) current - else { - val result = - if (es == null) { - es = Array.fill(poly.paramNames.length)(initial) - updateEntries(current, poly, es) - } - else if (es ne entries(prev, poly)) - current // can re-use existing entries array. - else { - es = es.clone - updateEntries(current, poly, es) - } - es(idx) = entry - result - } - } - - def update(prev: OrderingConstraint, current: OrderingConstraint, - param: PolyParam, entry: T)(implicit ctx: Context): OrderingConstraint = - update(prev, current, param.binder, param.paramNum, entry) - - def map(prev: OrderingConstraint, current: OrderingConstraint, - poly: PolyType, idx: Int, f: T => T)(implicit ctx: Context): OrderingConstraint = - update(prev, current, poly, idx, f(apply(current, poly, idx))) - - def map(prev: OrderingConstraint, current: OrderingConstraint, - param: PolyParam, f: T => T)(implicit ctx: Context): OrderingConstraint = - map(prev, current, param.binder, param.paramNum, f) - } - - val boundsLens = new ConstraintLens[Type] { - def entries(c: OrderingConstraint, poly: PolyType): Array[Type] = - c.boundsMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[Type])(implicit ctx: Context): OrderingConstraint = - newConstraint(c.boundsMap.updated(poly, entries), c.lowerMap, c.upperMap) - def initial = NoType - } - - val lowerLens = new ConstraintLens[List[PolyParam]] { - def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] = - c.lowerMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint = - newConstraint(c.boundsMap, c.lowerMap.updated(poly, entries), c.upperMap) - def initial = Nil - } - - val upperLens = new ConstraintLens[List[PolyParam]] { - def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] = - c.upperMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint = - newConstraint(c.boundsMap, c.lowerMap, c.upperMap.updated(poly, entries)) - def initial = Nil - } -} - -import OrderingConstraint._ - -/** Constraint over undetermined type parameters that keeps separate maps to - * reflect parameter orderings. - * @param boundsMap a map from PolyType to arrays. - * Each array contains twice the number of entries as there a type parameters - * in the PolyType. The first half of the array contains the type bounds that constrain the - * polytype's type parameters. The second half might contain type variables that - * track the corresponding parameters, or is left empty (filled with nulls). - * An instantiated type parameter is represented by having its instance type in - * the corresponding array entry. The dual use of arrays for poly params - * and typevars is to save space and hopefully gain some speed. - * - * @param lowerMap a map from PolyTypes to arrays. Each array entry corresponds - * to a parameter P of the polytype; it contains all constrained parameters - * Q that are known to be smaller than P, i.e. Q <: P. - * @param upperMap a map from PolyTypes to arrays. Each array entry corresponds - * to a parameter P of the polytype; it contains all constrained parameters - * Q that are known to be greater than P, i.e. P <: Q. - */ -class OrderingConstraint(private val boundsMap: ParamBounds, - private val lowerMap : ParamOrdering, - private val upperMap : ParamOrdering) extends Constraint { - - type This = OrderingConstraint - -// ----------- Basic indices -------------------------------------------------- - - /** The number of type parameters in the given entry array */ - private def paramCount(entries: Array[Type]) = entries.length >> 1 - - /** The type variable corresponding to parameter numbered `n`, null if none was created */ - private def typeVar(entries: Array[Type], n: Int): Type = - entries(paramCount(entries) + n) - - /** The `boundsMap` entry corresponding to `param` */ - def entry(param: PolyParam): Type = { - val entries = boundsMap(param.binder) - if (entries == null) NoType - else entries(param.paramNum) - } - -// ----------- Contains tests -------------------------------------------------- - - def contains(pt: PolyType): Boolean = boundsMap(pt) != null - - def contains(param: PolyParam): Boolean = { - val entries = boundsMap(param.binder) - entries != null && isBounds(entries(param.paramNum)) - } - - def contains(tvar: TypeVar): Boolean = { - val origin = tvar.origin - val entries = boundsMap(origin.binder) - val pnum = origin.paramNum - entries != null && isBounds(entries(pnum)) && (typeVar(entries, pnum) eq tvar) - } - - private def isBounds(tp: Type) = tp.isInstanceOf[TypeBounds] - -// ---------- Dependency handling ---------------------------------------------- - - def lower(param: PolyParam): List[PolyParam] = lowerLens(this, param.binder, param.paramNum) - def upper(param: PolyParam): List[PolyParam] = upperLens(this, param.binder, param.paramNum) - - def minLower(param: PolyParam): List[PolyParam] = { - val all = lower(param) - all.filterNot(p => all.exists(isLess(p, _))) - } - - def minUpper(param: PolyParam): List[PolyParam] = { - val all = upper(param) - all.filterNot(p => all.exists(isLess(_, p))) - } - - def exclusiveLower(param: PolyParam, butNot: PolyParam): List[PolyParam] = - lower(param).filterNot(isLess(_, butNot)) - - def exclusiveUpper(param: PolyParam, butNot: PolyParam): List[PolyParam] = - upper(param).filterNot(isLess(butNot, _)) - -// ---------- Info related to PolyParams ------------------------------------------- - - def isLess(param1: PolyParam, param2: PolyParam): Boolean = - upper(param1).contains(param2) - - def nonParamBounds(param: PolyParam): TypeBounds = - entry(param).asInstanceOf[TypeBounds] - - def fullLowerBound(param: PolyParam)(implicit ctx: Context): Type = - (nonParamBounds(param).lo /: minLower(param))(_ | _) - - def fullUpperBound(param: PolyParam)(implicit ctx: Context): Type = - (nonParamBounds(param).hi /: minUpper(param))(_ & _) - - def fullBounds(param: PolyParam)(implicit ctx: Context): TypeBounds = - nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param)) - - def typeVarOfParam(param: PolyParam): Type = { - val entries = boundsMap(param.binder) - if (entries == null) NoType - else { - val tvar = typeVar(entries, param.paramNum) - if (tvar != null) tvar else NoType - } - } - -// ---------- Adding PolyTypes -------------------------------------------------- - - /** The list of parameters P such that, for a fresh type parameter Q: - * - * Q <: tp implies Q <: P and isUpper = true, or - * tp <: Q implies P <: Q and isUpper = false - */ - def dependentParams(tp: Type, isUpper: Boolean): List[PolyParam] = tp match { - case param: PolyParam if contains(param) => - param :: (if (isUpper) upper(param) else lower(param)) - case tp: AndOrType => - val ps1 = dependentParams(tp.tp1, isUpper) - val ps2 = dependentParams(tp.tp2, isUpper) - if (isUpper == tp.isAnd) ps1.union(ps2) else ps1.intersect(ps2) - case _ => - Nil - } - - /** The bound type `tp` without constrained parameters which are clearly - * dependent. A parameter in an upper bound is clearly dependent if it appears - * in a hole of a context H given by: - * - * H = [] - * H & T - * T & H - * - * (the idea is that a parameter P in a H context is guaranteed to be a supertype of the - * bounded parameter.) - * Analogously, a parameter in a lower bound is clearly dependent if it appears - * in a hole of a context H given by: - * - * L = [] - * L | T - * T | L - * - * "Clearly dependent" is not synonymous with "dependent" in the sense - * it is defined in `dependentParams`. Dependent parameters are handled - * in `updateEntry`. The idea of stripping off clearly dependent parameters - * and to handle them separately is for efficiency, so that type expressions - * used as bounds become smaller. - * - * @param isUpper If true, `bound` is an upper bound, else a lower bound. - */ - private def stripParams(tp: Type, paramBuf: mutable.ListBuffer[PolyParam], - isUpper: Boolean)(implicit ctx: Context): Type = tp match { - case param: PolyParam if contains(param) => - if (!paramBuf.contains(param)) paramBuf += param - NoType - case tp: AndOrType if isUpper == tp.isAnd => - val tp1 = stripParams(tp.tp1, paramBuf, isUpper) - val tp2 = stripParams(tp.tp2, paramBuf, isUpper) - if (tp1.exists) - if (tp2.exists) tp.derivedAndOrType(tp1, tp2) - else tp1 - else tp2 - case _ => - tp - } - - /** The bound type `tp` without clearly dependent parameters. - * A top or bottom type if type consists only of dependent parameters. - * @param isUpper If true, `bound` is an upper bound, else a lower bound. - */ - private def normalizedType(tp: Type, paramBuf: mutable.ListBuffer[PolyParam], - isUpper: Boolean)(implicit ctx: Context): Type = - stripParams(tp, paramBuf, isUpper) - .orElse(if (isUpper) defn.AnyType else defn.NothingType) - - def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This = { - assert(!contains(poly)) - val nparams = poly.paramNames.length - val entries1 = new Array[Type](nparams * 2) - poly.paramBounds.copyToArray(entries1, 0) - tvars.copyToArray(entries1, nparams) - newConstraint(boundsMap.updated(poly, entries1), lowerMap, upperMap).init(poly) - } - - /** Split dependent parameters off the bounds for parameters in `poly`. - * Update all bounds to be normalized and update ordering to account for - * dependent parameters. - */ - private def init(poly: PolyType)(implicit ctx: Context): This = { - var current = this - val loBuf, hiBuf = new mutable.ListBuffer[PolyParam] - var i = 0 - while (i < poly.paramNames.length) { - val param = PolyParam(poly, i) - val bounds = nonParamBounds(param) - val lo = normalizedType(bounds.lo, loBuf, isUpper = false) - val hi = normalizedType(bounds.hi, hiBuf, isUpper = true) - current = updateEntry(current, param, bounds.derivedTypeBounds(lo, hi)) - current = (current /: loBuf)(order(_, _, param)) - current = (current /: hiBuf)(order(_, param, _)) - loBuf.clear() - hiBuf.clear() - i += 1 - } - if (Config.checkConstraintsNonCyclic) checkNonCyclic() - current - } - -// ---------- Updates ------------------------------------------------------------ - - /** Add the fact `param1 <: param2` to the constraint `current` and propagate - * `<:<` relationships between parameters ("edges") but not bounds. - */ - private def order(current: This, param1: PolyParam, param2: PolyParam)(implicit ctx: Context): This = - if (param1 == param2 || current.isLess(param1, param2)) this - else { - assert(contains(param1)) - assert(contains(param2)) - val newUpper = param2 :: exclusiveUpper(param2, param1) - val newLower = param1 :: exclusiveLower(param1, param2) - val current1 = (current /: newLower)(upperLens.map(this, _, _, newUpper ::: _)) - val current2 = (current1 /: newUpper)(lowerLens.map(this, _, _, newLower ::: _)) - current2 - } - - def addLess(param1: PolyParam, param2: PolyParam)(implicit ctx: Context): This = - order(this, param1, param2) - - def updateEntry(current: This, param: PolyParam, tp: Type)(implicit ctx: Context): This = { - var current1 = boundsLens.update(this, current, param, tp) - tp match { - case TypeBounds(lo, hi) => - for (p <- dependentParams(lo, isUpper = false)) - current1 = order(current1, p, param) - for (p <- dependentParams(hi, isUpper = true)) - current1 = order(current1, param, p) - case _ => - } - current1 - } - - def updateEntry(param: PolyParam, tp: Type)(implicit ctx: Context): This = - updateEntry(this, param, tp) - - def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This = { - val p1Bounds = (nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1) - updateEntry(p1, p1Bounds).replace(p2, p1) - } - - def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This = { - val oldBounds @ TypeBounds(lo, hi) = nonParamBounds(param) - val newBounds = - if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound) - else oldBounds.derivedTypeBounds(lo | bound, hi) - updateEntry(param, newBounds) - } - -// ---------- Removals ------------------------------------------------------------ - - /** A new constraint which is derived from this constraint by removing - * the type parameter `param` from the domain and replacing all top-level occurrences - * of the parameter elsewhere in the constraint by type `tp`, or a conservative - * approximation of it if that is needed to avoid cycles. - * Occurrences nested inside a refinement or prefix are not affected. - * - * The reason we need to substitute top-level occurrences of the parameter - * is to deal with situations like the following. Say we have in the constraint - * - * P <: Q & String - * Q - * - * and we replace Q with P. Then substitution gives - * - * P <: P & String - * - * this would be a cyclic constraint is therefore changed by `normalize` and - * `recombine` below to - * - * P <: String - * - * approximating the RHS occurrence of P with Any. Without the substitution we - * would not find out where we need to approximate. Occurrences of parameters - * that are not top-level are not affected. - */ - def replace(param: PolyParam, tp: Type)(implicit ctx: Context): OrderingConstraint = { - val replacement = tp.dealias.stripTypeVar - if (param == replacement) this - else { - assert(replacement.isValueTypeOrLambda) - val poly = param.binder - val idx = param.paramNum - - def removeParam(ps: List[PolyParam]) = - ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx) - - def replaceParam(tp: Type, atPoly: PolyType, atIdx: Int): Type = tp match { - case bounds @ TypeBounds(lo, hi) => - - def recombine(andor: AndOrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = { - val tp1 = op(andor.tp1, isUpper) - val tp2 = op(andor.tp2, isUpper) - if ((tp1 eq andor.tp1) && (tp2 eq andor.tp2)) andor - else if (andor.isAnd) tp1 & tp2 - else tp1 | tp2 - } - - def normalize(tp: Type, isUpper: Boolean): Type = tp match { - case p: PolyParam if p.binder == atPoly && p.paramNum == atIdx => - if (isUpper) defn.AnyType else defn.NothingType - case tp: AndOrType if isUpper == tp.isAnd => recombine(tp, normalize, isUpper) - case _ => tp - } - - def replaceIn(tp: Type, isUpper: Boolean): Type = tp match { - case `param` => normalize(replacement, isUpper) - case tp: AndOrType if isUpper == tp.isAnd => recombine(tp, replaceIn, isUpper) - case _ => tp.substParam(param, replacement) - } - - bounds.derivedTypeBounds(replaceIn(lo, isUpper = false), replaceIn(hi, isUpper = true)) - case _ => - tp.substParam(param, replacement) - } - - var current = - if (isRemovable(poly)) remove(poly) else updateEntry(param, replacement) - current.foreachParam {(p, i) => - current = boundsLens.map(this, current, p, i, replaceParam(_, p, i)) - current = lowerLens.map(this, current, p, i, removeParam) - current = upperLens.map(this, current, p, i, removeParam) - } - current - } - } - - def remove(pt: PolyType)(implicit ctx: Context): This = { - def removeFromOrdering(po: ParamOrdering) = { - def removeFromBoundss(key: PolyType, bndss: Array[List[PolyParam]]): Array[List[PolyParam]] = { - val bndss1 = bndss.map(_.filterConserve(_.binder ne pt)) - if (bndss.corresponds(bndss1)(_ eq _)) bndss else bndss1 - } - po.remove(pt).mapValuesNow(removeFromBoundss) - } - newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap)) - } - - def isRemovable(pt: PolyType): Boolean = { - val entries = boundsMap(pt) - @tailrec def allRemovable(last: Int): Boolean = - if (last < 0) true - else typeVar(entries, last) match { - case tv: TypeVar => tv.inst.exists && allRemovable(last - 1) - case _ => false - } - allRemovable(paramCount(entries) - 1) - } - -// ---------- Exploration -------------------------------------------------------- - - def domainPolys: List[PolyType] = boundsMap.keys - - def domainParams: List[PolyParam] = - for { - (poly, entries) <- boundsMap.toList - n <- 0 until paramCount(entries) - if entries(n).exists - } yield PolyParam(poly, n) - - def forallParams(p: PolyParam => Boolean): Boolean = { - boundsMap.foreachBinding { (poly, entries) => - for (i <- 0 until paramCount(entries)) - if (isBounds(entries(i)) && !p(PolyParam(poly, i))) return false - } - true - } - - def foreachParam(p: (PolyType, Int) => Unit): Unit = - boundsMap.foreachBinding { (poly, entries) => - 0.until(poly.paramNames.length).foreach(p(poly, _)) - } - - def foreachTypeVar(op: TypeVar => Unit): Unit = - boundsMap.foreachBinding { (poly, entries) => - for (i <- 0 until paramCount(entries)) { - typeVar(entries, i) match { - case tv: TypeVar if !tv.inst.exists => op(tv) - case _ => - } - } - } - - def & (other: Constraint)(implicit ctx: Context) = { - def merge[T](m1: ArrayValuedMap[T], m2: ArrayValuedMap[T], join: (T, T) => T): ArrayValuedMap[T] = { - var merged = m1 - def mergeArrays(xs1: Array[T], xs2: Array[T]) = { - val xs = xs1.clone - for (i <- xs.indices) xs(i) = join(xs1(i), xs2(i)) - xs - } - m2.foreachBinding { (poly, xs2) => - merged = merged.updated(poly, - if (m1.contains(poly)) mergeArrays(m1(poly), xs2) else xs2) - } - merged - } - - def mergeParams(ps1: List[PolyParam], ps2: List[PolyParam]) = - (ps1 /: ps2)((ps1, p2) => if (ps1.contains(p2)) ps1 else p2 :: ps1) - - def mergeEntries(e1: Type, e2: Type): Type = e1 match { - case e1: TypeBounds => - e2 match { - case e2: TypeBounds => e1 & e2 - case _ if e1 contains e2 => e2 - case _ => mergeError - } - case tv1: TypeVar => - e2 match { - case tv2: TypeVar if tv1.instanceOpt eq tv2.instanceOpt => e1 - case _ => mergeError - } - case _ if e1 eq e2 => e1 - case _ => mergeError - } - - def mergeError = throw new AssertionError(i"cannot merge $this with $other") - - val that = other.asInstanceOf[OrderingConstraint] - new OrderingConstraint( - merge(this.boundsMap, that.boundsMap, mergeEntries), - merge(this.lowerMap, that.lowerMap, mergeParams), - merge(this.upperMap, that.upperMap, mergeParams)) - } - - override def checkClosed()(implicit ctx: Context): Unit = { - def isFreePolyParam(tp: Type) = tp match { - case PolyParam(binder: PolyType, _) => !contains(binder) - case _ => false - } - def checkClosedType(tp: Type, where: String) = - if (tp != null) - assert(!tp.existsPart(isFreePolyParam), i"unclosed constraint: $this refers to $tp in $where") - boundsMap.foreachBinding((_, tps) => tps.foreach(checkClosedType(_, "bounds"))) - lowerMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "lower")))) - upperMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "upper")))) - } - - private var myUninstVars: mutable.ArrayBuffer[TypeVar] = _ - - /** The uninstantiated typevars of this constraint */ - def uninstVars: collection.Seq[TypeVar] = { - if (myUninstVars == null) { - myUninstVars = new mutable.ArrayBuffer[TypeVar] - boundsMap.foreachBinding { (poly, entries) => - for (i <- 0 until paramCount(entries)) { - typeVar(entries, i) match { - case tv: TypeVar if !tv.inst.exists && isBounds(entries(i)) => myUninstVars += tv - case _ => - } - } - } - } - myUninstVars - } - -// ---------- Cyclic checking ------------------------------------------- - - def checkNonCyclic()(implicit ctx: Context): Unit = - domainParams.foreach(checkNonCyclic) - - private def checkNonCyclic(param: PolyParam)(implicit ctx: Context): Unit = - assert(!isLess(param, param), i"cyclic constraint involving $param in $this") - -// ---------- toText ----------------------------------------------------- - - override def toText(printer: Printer): Text = { - def entryText(tp: Type) = tp match { - case tp: TypeBounds => - tp.toText(printer) - case _ => - " := " ~ tp.toText(printer) - } - val indent = 3 - val header: Text = "Constraint(" - val uninstVarsText = " uninstVars = " ~ - Text(uninstVars map (_.toText(printer)), ", ") ~ ";" - val constrainedText = - " constrained types = " ~ Text(domainPolys map (_.toText(printer)), ", ") - val boundsText = - " bounds = " ~ { - val assocs = - for (param <- domainParams) - yield (" " * indent) ~ param.toText(printer) ~ entryText(entry(param)) - Text(assocs, "\n") - } - val orderingText = - " ordering = " ~ { - val deps = - for { - param <- domainParams - ups = minUpper(param) - if ups.nonEmpty - } - yield - (" " * indent) ~ param.toText(printer) ~ " <: " ~ - Text(ups.map(_.toText(printer)), ", ") - Text(deps, "\n") - } - Text.lines(List(header, uninstVarsText, constrainedText, boundsText, orderingText, ")")) - } - - override def toString: String = { - def entryText(tp: Type): String = tp match { - case tp: TypeBounds => tp.toString - case _ =>" := " + tp - } - val constrainedText = - " constrained types = " + domainPolys.mkString("\n") - val boundsText = - " bounds = " + { - val assocs = - for (param <- domainParams) - yield - param.binder.paramNames(param.paramNum) + ": " + entryText(entry(param)) - assocs.mkString("\n") - } - constrainedText + "\n" + boundsText - } -} diff --git a/src/dotty/tools/dotc/core/Periods.scala b/src/dotty/tools/dotc/core/Periods.scala deleted file mode 100644 index 6efadab7f..000000000 --- a/src/dotty/tools/dotc/core/Periods.scala +++ /dev/null @@ -1,159 +0,0 @@ -package dotty.tools.dotc.core - -import Contexts._ -import dotty.tools.dotc.util.DotClass - -/** Periods are the central "clock" of the compiler. - * A period consists of a run id and a phase id. - * run ids represent compiler runs - * phase ids represent compiler phases - */ -abstract class Periods extends DotClass { self: Context => - import Periods._ - - /** The current phase identifier */ - def phaseId: Int = period.phaseId - - /** The current run identifier */ - def runId: Int = period.runId - - /** Execute `op` at given period */ - def atPeriod[T](pd: Period)(op: Context => T): T = - op(ctx.fresh.setPeriod(pd)) - - /** Execute `op` at given phase id */ - def atPhase[T](pid: PhaseId)(op: Context => T): T = - op(ctx.withPhase(pid)) - - /** The period containing the current period where denotations do not change. - * We compute this by taking as first phase the first phase less or equal to - * the current phase that has the same "nextTransformerId". As last phase - * we take the next transformer id following the current phase. - */ - def stablePeriod = { - var first = phaseId - val nxTrans = ctx.base.nextDenotTransformerId(first) - while (first - 1 > NoPhaseId && (ctx.base.nextDenotTransformerId(first - 1) == nxTrans)) { - first -= 1 - } - Period(runId, first, nxTrans) - } -} - -object Periods { - - /** A period is a contiguous sequence of phase ids in some run. - * It is coded as follows: - * - * sign, always 0 1 bit - * runid 19 bits - * last phase id: 6 bits - * #phases before last: 6 bits - * - * // Dmitry: sign == 0 isn't actually always true, in some cases phaseId == -1 is used for shifts, that easily creates code < 0 - */ - class Period(val code: Int) extends AnyVal { - - /** The run identifier of this period. */ - def runId: RunId = code >>> (PhaseWidth * 2) - - /** The phase identifier of this single-phase period. */ - def phaseId: PhaseId = (code >>> PhaseWidth) & PhaseMask - - /** The last phase of this period */ - def lastPhaseId: PhaseId = - (code >>> PhaseWidth) & PhaseMask - - /** The first phase of this period */ - def firstPhaseId = lastPhaseId - (code & PhaseMask) - - def containsPhaseId(id: PhaseId) = firstPhaseId <= id && id <= lastPhaseId - - /** Does this period contain given period? */ - def contains(that: Period): Boolean = { - // Let this = (r1, l1, d1), that = (r2, l2, d2) - // where r = runid, l = last phase, d = duration - 1 - // Then seen as intervals: - // - // this = r1 / (l1 - d1) .. l1 - // that = r2 / (l2 - d2) .. l2 - // - // Let's compute: - // - // lastDiff = X * 2^5 + (l1 - l2) mod 2^5 - // where X >= 0, X == 0 iff r1 == r2 & l1 - l2 >= 0 - // result = lastDiff + d2 <= d1 - // We have: - // lastDiff + d2 <= d1 - // iff X == 0 && l1 - l2 >= 0 && l1 - l2 + d2 <= d1 - // iff r1 == r2 & l1 >= l2 && l1 - d1 <= l2 - d2 - // q.e.d - val lastDiff = (code - that.code) >>> PhaseWidth - lastDiff + (that.code & PhaseMask ) <= (this.code & PhaseMask) - } - - /** Does this period overlap with given period? */ - def overlaps(that: Period): Boolean = - this.runId == that.runId && - this.firstPhaseId <= that.lastPhaseId && - that.firstPhaseId <= this.lastPhaseId - - /** The intersection of two periods */ - def & (that: Period): Period = - if (this overlaps that) - Period( - this.runId, - this.firstPhaseId max that.firstPhaseId, - this.lastPhaseId min that.lastPhaseId) - else - Nowhere - - def | (that: Period): Period = - Period(this.runId, - this.firstPhaseId min that.firstPhaseId, - this.lastPhaseId max that.lastPhaseId) - - override def toString = s"Period($firstPhaseId..$lastPhaseId, run = $runId)" - } - - object Period { - - /** The single-phase period consisting of given run id and phase id */ - def apply(rid: RunId, pid: PhaseId): Period = { - new Period(((rid << PhaseWidth) | pid) << PhaseWidth) - } - - /** The period consisting of given run id, and lo/hi phase ids */ - def apply(rid: RunId, loPid: PhaseId, hiPid: PhaseId): Period = { - new Period(((rid << PhaseWidth) | hiPid) << PhaseWidth | (hiPid - loPid)) - } - - /** The interval consisting of all periods of given run id */ - def allInRun(rid: RunId) = { - apply(rid, 0, PhaseMask) - } - } - - final val Nowhere = new Period(0) - - final val InitialPeriod = Period(InitialRunId, FirstPhaseId) - - final val InvalidPeriod = Period(NoRunId, NoPhaseId) - - /** An ordinal number for compiler runs. First run has number 1. */ - type RunId = Int - final val NoRunId = 0 - final val InitialRunId = 1 - final val RunWidth = java.lang.Integer.SIZE - PhaseWidth * 2 - 1/* sign */ - final val MaxPossibleRunId = (1 << RunWidth) - 1 - - /** An ordinal number for phases. First phase has number 1. */ - type PhaseId = Int - final val NoPhaseId = 0 - final val FirstPhaseId = 1 - - /** The number of bits needed to encode a phase identifier. */ - final val PhaseWidth = 6 - final val PhaseMask = (1 << PhaseWidth) - 1 - final val MaxPossiblePhaseId = PhaseMask -} diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala deleted file mode 100644 index 222e2235d..000000000 --- a/src/dotty/tools/dotc/core/Phases.scala +++ /dev/null @@ -1,377 +0,0 @@ -package dotty.tools.dotc -package core - -import Periods._ -import Contexts._ -import dotty.tools.backend.jvm.{LabelDefs, GenBCode} -import dotty.tools.dotc.core.Symbols.ClassSymbol -import util.DotClass -import DenotTransformers._ -import Denotations._ -import Decorators._ -import config.Printers.config -import scala.collection.mutable.{ListBuffer, ArrayBuffer} -import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform} -import dotty.tools.dotc.transform._ -import Periods._ -import typer.{FrontEnd, RefChecks} -import ast.tpd - -trait Phases { - self: Context => - - import Phases._ - - def phase: Phase = base.phases(period.firstPhaseId) - - def phasesStack: List[Phase] = - if ((this eq NoContext) || !phase.exists) Nil - else phase :: outersIterator.dropWhile(_.phase == phase).next.phasesStack - - /** Execute `op` at given phase */ - def atPhase[T](phase: Phase)(op: Context => T): T = - atPhase(phase.id)(op) - - def atNextPhase[T](op: Context => T): T = atPhase(phase.next)(op) - - def atPhaseNotLaterThan[T](limit: Phase)(op: Context => T): T = - if (!limit.exists || phase <= limit) op(this) else atPhase(limit)(op) - - def atPhaseNotLaterThanTyper[T](op: Context => T): T = - atPhaseNotLaterThan(base.typerPhase)(op) - - def isAfterTyper: Boolean = base.isAfterTyper(phase) -} - -object Phases { - - trait PhasesBase { - this: ContextBase => - - // drop NoPhase at beginning - def allPhases = (if (squashedPhases.nonEmpty) squashedPhases else phases).tail - - object NoPhase extends Phase { - override def exists = false - def phaseName = "<no phase>" - def run(implicit ctx: Context): Unit = unsupported("run") - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = unsupported("transform") - } - - object SomePhase extends Phase { - def phaseName = "<some phase>" - def run(implicit ctx: Context): Unit = unsupported("run") - } - - /** A sentinel transformer object */ - class TerminalPhase extends DenotTransformer { - def phaseName = "terminal" - def run(implicit ctx: Context): Unit = unsupported("run") - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = - unsupported("transform") - override def lastPhaseId(implicit ctx: Context) = id - } - - def phasePlan = this.phasesPlan - def setPhasePlan(phasess: List[List[Phase]]) = this.phasesPlan = phasess - - /** Squash TreeTransform's beloning to same sublist to a single TreeTransformer - * Each TreeTransform gets own period, - * whereas a combined TreeTransformer gets period equal to union of periods of it's TreeTransforms - */ - def squashPhases(phasess: List[List[Phase]], - phasesToSkip: List[String], stopBeforePhases: List[String], stopAfterPhases: List[String], YCheckAfter: List[String]): List[Phase] = { - val squashedPhases = ListBuffer[Phase]() - var prevPhases: Set[Class[_ <: Phase]] = Set.empty - val YCheckAll = YCheckAfter.contains("all") - - var stop = false - val filteredPhases = phasess.map(_.filter { p => - val pstop = stop - stop = stop | stopBeforePhases.contains(p.phaseName) | stopAfterPhases.contains(p.phaseName) - !(pstop || stopBeforePhases.contains(p.phaseName) || phasesToSkip.contains(p.phaseName)) - }) - - var i = 0 - - while (i < filteredPhases.length) { - if (filteredPhases(i).nonEmpty) { //could be empty due to filtering - val filteredPhaseBlock = filteredPhases(i) - val phaseToAdd = - if (filteredPhaseBlock.length > 1) { - val phasesInBlock: Set[String] = filteredPhaseBlock.map(_.phaseName).toSet - for (phase <- filteredPhaseBlock) { - phase match { - case p: MiniPhase => - val unmetRequirements = p.runsAfterGroupsOf &~ prevPhases - assert(unmetRequirements.isEmpty, - s"${phase.phaseName} requires ${unmetRequirements.mkString(", ")} to be in different TreeTransformer") - - case _ => - assert(false, s"Only tree transforms can be squashed, ${phase.phaseName} can not be squashed") - } - } - val block = new TreeTransformer { - override def phaseName: String = miniPhases.map(_.phaseName).mkString("TreeTransform:{", ", ", "}") - override def miniPhases: Array[MiniPhase] = filteredPhaseBlock.asInstanceOf[List[MiniPhase]].toArray - } - prevPhases ++= filteredPhaseBlock.map(_.getClazz) - block - } else { // block of a single phase, no squashing - val phase = filteredPhaseBlock.head - prevPhases += phase.getClazz - phase - } - squashedPhases += phaseToAdd - val shouldAddYCheck = YCheckAfter.containsPhase(phaseToAdd) || YCheckAll - if (shouldAddYCheck) { - val checker = new TreeChecker - squashedPhases += checker - } - } - - i += 1 - } - squashedPhases.toList - } - - /** Use the following phases in the order they are given. - * The list should never contain NoPhase. - * if squashing is enabled, phases in same subgroup will be squashed to single phase. - */ - def usePhases(phasess: List[Phase], squash: Boolean = true) = { - - val flatPhases = collection.mutable.ListBuffer[Phase]() - - phasess.foreach(p => p match { - case t: TreeTransformer => flatPhases ++= t.miniPhases - case _ => flatPhases += p - }) - - phases = (NoPhase :: flatPhases.toList ::: new TerminalPhase :: Nil).toArray - var phasesAfter:Set[Class[_ <: Phase]] = Set.empty - nextDenotTransformerId = new Array[Int](phases.length) - denotTransformers = new Array[DenotTransformer](phases.length) - - var phaseId = 0 - def nextPhaseId = { - phaseId += 1 - phaseId // starting from 1 as NoPhase is 0 - } - - def checkRequirements(p: Phase) = { - val unmetPrecedeRequirements = p.runsAfter -- phasesAfter - assert(unmetPrecedeRequirements.isEmpty, - s"phase ${p} has unmet requirement: ${unmetPrecedeRequirements.mkString(", ")} should precede this phase") - phasesAfter += p.getClazz - - } - var i = 0 - - while (i < phasess.length) { - val phase = phasess(i) - phase match { - case t: TreeTransformer => - val miniPhases = t.miniPhases - miniPhases.foreach{ phase => - checkRequirements(phase) - phase.init(this, nextPhaseId)} - t.init(this, miniPhases.head.id, miniPhases.last.id) - case _ => - phase.init(this, nextPhaseId) - checkRequirements(phase) - } - - i += 1 - } - - phases.last.init(this, nextPhaseId) // init terminal phase - - i = phases.length - var lastTransformerId = i - while (i > 0) { - i -= 1 - val phase = phases(i) - phase match { - case transformer: DenotTransformer => - lastTransformerId = i - denotTransformers(i) = transformer - case _ => - } - nextDenotTransformerId(i) = lastTransformerId - } - - if (squash) { - this.squashedPhases = (NoPhase :: phasess).toArray - } else { - this.squashedPhases = this.phases - } - - config.println(s"Phases = ${phases.deep}") - config.println(s"nextDenotTransformerId = ${nextDenotTransformerId.deep}") - } - - def phaseOfClass(pclass: Class[_]) = phases.find(pclass.isInstance).getOrElse(NoPhase) - - private val cachedPhases = collection.mutable.Set[PhaseCache]() - private def cleanPhaseCache = cachedPhases.foreach(_.myPhase = NoPhase) - - /** A cache to compute the phase with given name, which - * stores the phase as soon as phaseNamed returns something - * different from NoPhase. - */ - private class PhaseCache(pclass: Class[_ <: Phase]) { - var myPhase: Phase = NoPhase - def phase = { - if (myPhase eq NoPhase) myPhase = phaseOfClass(pclass) - myPhase - } - cachedPhases += this - } - - private val typerCache = new PhaseCache(classOf[FrontEnd]) - private val picklerCache = new PhaseCache(classOf[Pickler]) - - private val refChecksCache = new PhaseCache(classOf[RefChecks]) - private val elimRepeatedCache = new PhaseCache(classOf[ElimRepeated]) - private val extensionMethodsCache = new PhaseCache(classOf[ExtensionMethods]) - private val erasureCache = new PhaseCache(classOf[Erasure]) - private val elimErasedValueTypeCache = new PhaseCache(classOf[ElimErasedValueType]) - private val patmatCache = new PhaseCache(classOf[PatternMatcher]) - private val lambdaLiftCache = new PhaseCache(classOf[LambdaLift]) - private val flattenCache = new PhaseCache(classOf[Flatten]) - private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter]) - private val gettersCache = new PhaseCache(classOf[Getters]) - private val genBCodeCache = new PhaseCache(classOf[GenBCode]) - - def typerPhase = typerCache.phase - def picklerPhase = picklerCache.phase - def refchecksPhase = refChecksCache.phase - def elimRepeatedPhase = elimRepeatedCache.phase - def extensionMethodsPhase = extensionMethodsCache.phase - def erasurePhase = erasureCache.phase - def elimErasedValueTypePhase = elimErasedValueTypeCache.phase - def patmatPhase = patmatCache.phase - def lambdaLiftPhase = lambdaLiftCache.phase - def flattenPhase = flattenCache.phase - def explicitOuterPhase = explicitOuterCache.phase - def gettersPhase = gettersCache.phase - def genBCodePhase = genBCodeCache.phase - - def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id - } - - trait Phase extends DotClass { - - def phaseName: String - - /** List of names of phases that should precede this phase */ - def runsAfter: Set[Class[_ <: Phase]] = Set.empty - - def run(implicit ctx: Context): Unit - - def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = - units.map { unit => - val unitCtx = ctx.fresh.setPhase(this.start).setCompilationUnit(unit) - run(unitCtx) - unitCtx.compilationUnit - } - - def description: String = phaseName - - /** Output should be checkable by TreeChecker */ - def isCheckable: Boolean = true - - /** Check what the phase achieves, to be called at any point after it is finished. - */ - def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = () - - /** If set, allow missing or superfluous arguments in applications - * and type applications. - */ - def relaxedTyping: Boolean = false - - /** Is this phase the standard typerphase? True for FrontEnd, but - * not for other first phases (such as FromTasty). The predicate - * is tested in some places that perform checks and corrections. It's - * different from isAfterTyper (and cheaper to test). - */ - def isTyper = false - - def exists: Boolean = true - - private var myPeriod: Period = Periods.InvalidPeriod - private var myBase: ContextBase = null - private var myErasedTypes = false - private var myFlatClasses = false - private var myRefChecked = false - private var mySymbolicRefs = false - private var myLabelsReordered = false - - - /** The sequence position of this phase in the given context where 0 - * is reserved for NoPhase and the first real phase is at position 1. - * -1 if the phase is not installed in the context. - */ - def id = myPeriod.firstPhaseId - - def period = myPeriod - def start = myPeriod.firstPhaseId - def end = myPeriod.lastPhaseId - - final def erasedTypes = myErasedTypes // Phase is after erasure - final def flatClasses = myFlatClasses // Phase is after flatten - final def refChecked = myRefChecked // Phase is after RefChecks - final def symbolicRefs = mySymbolicRefs // Phase is after ResolveSuper, newly generated TermRefs should be symbolic - final def labelsReordered = myLabelsReordered // Phase is after LabelDefs, labels are flattened and owner chains don't mirror this - - protected[Phases] def init(base: ContextBase, start: Int, end:Int): Unit = { - if (start >= FirstPhaseId) - assert(myPeriod == Periods.InvalidPeriod, s"phase $this has already been used once; cannot be reused") - myBase = base - myPeriod = Period(NoRunId, start, end) - myErasedTypes = prev.getClass == classOf[Erasure] || prev.erasedTypes - myFlatClasses = prev.getClass == classOf[Flatten] || prev.flatClasses - myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked - mySymbolicRefs = prev.getClass == classOf[ResolveSuper] || prev.symbolicRefs - myLabelsReordered = prev.getClass == classOf[LabelDefs] || prev.labelsReordered - } - - protected[Phases] def init(base: ContextBase, id: Int): Unit = init(base, id, id) - - final def <=(that: Phase) = - exists && id <= that.id - - final def prev: Phase = - if (id > FirstPhaseId) myBase.phases(start - 1) else myBase.NoPhase - - final def next: Phase = - if (hasNext) myBase.phases(end + 1) else myBase.NoPhase - - final def hasNext = start >= FirstPhaseId && end + 1 < myBase.phases.length - - final def iterator = - Iterator.iterate(this)(_.next) takeWhile (_.hasNext) - - override def toString = phaseName - } - - trait NeedsCompanions { - def isCompanionNeeded(cls: ClassSymbol)(implicit ctx: Context): Boolean - } - - /** Replace all instances of `oldPhaseClass` in `current` phases - * by the result of `newPhases` applied to the old phase. - */ - def replace(oldPhaseClass: Class[_ <: Phase], newPhases: Phase => List[Phase], current: List[List[Phase]]): List[List[Phase]] = - current.map(_.flatMap(phase => - if (oldPhaseClass.isInstance(phase)) newPhases(phase) else phase :: Nil)) - - /** Dotty deviation: getClass yields Class[_], instead of [Class <: <type of receiver>]. - * We can get back the old behavior using this decorator. We should also use the same - * trick for standard getClass. - */ - private implicit class getClassDeco[T](val x: T) extends AnyVal { - def getClazz: Class[_ <: T] = x.getClass.asInstanceOf[Class[_ <: T]] - } -} diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala deleted file mode 100644 index 3daa8117e..000000000 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ /dev/null @@ -1,437 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Martin Odersky - */ - -package dotty.tools.dotc -package core - -import Symbols._ -import Types.{TermRef, NoPrefix} -import Flags.Implicit -import Names._ -import Periods._ -import Decorators._ -import Contexts._ -import Denotations._ -import SymDenotations._ -import printing.Texts._ -import printing.Printer -import util.common._ -import util.DotClass -import SymDenotations.NoDenotation -import collection.mutable - -object Scopes { - - /** Maximal fill factor of hash table */ - private final val FillFactor = 2.0/3.0 - - /** A hashtable is created once current size exceeds MinHash * FillFactor - * The initial hash table has twice that size (i.e 16). - * This value must be a power of two, so that the index of an element can - * be computed as element.hashCode & (hashTable.length - 1) - */ - private final val MinHash = 8 - - /** The maximal permissible number of recursions when creating - * a hashtable - */ - private final val MaxRecursions = 1000 - - class ScopeEntry private[Scopes] (val name: Name, _sym: Symbol, val owner: Scope) { - - var sym: Symbol = _sym - - /** the next entry in the hash bucket - */ - var tail: ScopeEntry = null - - /** the preceding entry in this scope - */ - var prev: ScopeEntry = null - - override def toString: String = sym.toString - } - - /** A scope contains a set of symbols. It can be an extension - * of some outer scope, from which it inherits all symbols. - * This class does not have any methods to add symbols to a scope - * or to delete them. These methods are provided by subclass - * MutableScope. - */ - abstract class Scope extends DotClass with printing.Showable with Iterable[Symbol] { - - /** The last scope-entry from which all others are reachable via `prev` */ - private[dotc] def lastEntry: ScopeEntry - - /** The number of symbols in this scope (including inherited ones - * from outer scopes). - */ - def size: Int - - /** The number of outer scopes from which symbols are inherited */ - def nestingLevel: Int - - /** The symbols in this scope in the order they were entered; - * inherited from outer ones first. - */ - def toList: List[Symbol] - - /** Return all symbols as an iterator in the order they were entered in this scope. - */ - def iterator: Iterator[Symbol] = toList.iterator - - /** Returns a new mutable scope with the same content as this one. */ - def cloneScope(implicit ctx: Context): MutableScope - - /** Is the scope empty? */ - override def isEmpty: Boolean = lastEntry eq null - - /** Lookup a symbol entry matching given name. */ - def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry - - /** Lookup next entry with same name as this one */ - def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry - - /** Lookup a symbol */ - final def lookup(name: Name)(implicit ctx: Context): Symbol = { - val e = lookupEntry(name) - if (e eq null) NoSymbol else e.sym - } - - /** Returns an iterator yielding every symbol with given name in this scope. - */ - final def lookupAll(name: Name)(implicit ctx: Context): Iterator[Symbol] = new Iterator[Symbol] { - var e = lookupEntry(name) - def hasNext: Boolean = e ne null - def next(): Symbol = { val r = e.sym; e = lookupNextEntry(e); r } - } - - /** The denotation set of all the symbols with given name in this scope - * Symbols occur in the result in reverse order relative to their occurrence - * in `this.toList`. - */ - final def denotsNamed(name: Name, select: SymDenotation => Boolean = selectAll)(implicit ctx: Context): PreDenotation = { - var syms: PreDenotation = NoDenotation - var e = lookupEntry(name) - while (e != null) { - val d = e.sym.denot - if (select(d)) syms = syms union d - e = lookupNextEntry(e) - } - syms - } - - /** The scope that keeps only those symbols from this scope that match the - * given predicates. If all symbols match, returns the scope itself, otherwise - * a copy with the matching symbols. - */ - final def filteredScope(p: Symbol => Boolean)(implicit ctx: Context): Scope = { - var result: MutableScope = null - for (sym <- iterator) - if (!p(sym)) { - if (result == null) result = cloneScope - result.unlink(sym) - } - if (result == null) this else result - } - - def implicitDecls(implicit ctx: Context): List[TermRef] = Nil - - def openForMutations: MutableScope = unsupported("openForMutations") - - final def toText(printer: Printer): Text = printer.toText(this) - - def checkConsistent()(implicit ctx: Context) = () - } - - /** A subclass of Scope that defines methods for entering and - * unlinking entries. - * Note: constructor is protected to force everyone to use the factory methods newScope or newNestedScope instead. - * This is necessary because when run from reflection every scope needs to have a - * SynchronizedScope as mixin. - */ - class MutableScope protected[Scopes](initElems: ScopeEntry, initSize: Int, val nestingLevel: Int = 0) - extends Scope { - - protected[Scopes] def this(base: Scope)(implicit ctx: Context) = { - this(base.lastEntry, base.size, base.nestingLevel + 1) - ensureCapacity(MinHash)(ctx) // WTH? it seems the implicit is not in scope for a secondary constructor call. - } - - def this() = this(null, 0, 0) - - private[dotc] var lastEntry: ScopeEntry = initElems - - /** The size of the scope */ - private[this] var _size = initSize - - override final def size = _size - private def size_= (x: Int) = _size = x - - /** the hash table - */ - private var hashTable: Array[ScopeEntry] = null - - /** a cache for all elements, to be used by symbol iterator. - */ - private var elemsCache: List[Symbol] = null - - /** Clone scope, taking care not to force the denotations of any symbols in the scope. - */ - def cloneScope(implicit ctx: Context): MutableScope = { - val entries = new mutable.ArrayBuffer[ScopeEntry] - var e = lastEntry - while ((e ne null) && e.owner == this) { - entries += e - e = e.prev - } - val scope = newScope - for (i <- entries.length - 1 to 0 by -1) { - val e = entries(i) - scope.newScopeEntry(e.name, e.sym) - } - scope - } - - /** create and enter a scope entry with given name and symbol */ - protected def newScopeEntry(name: Name, sym: Symbol)(implicit ctx: Context): ScopeEntry = { - ensureCapacity(if (hashTable ne null) hashTable.length else MinHash) - val e = new ScopeEntry(name, sym, this) - e.prev = lastEntry - lastEntry = e - if (hashTable ne null) enterInHash(e) - size += 1 - elemsCache = null - e - } - - /** create and enter a scope entry */ - protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry = - newScopeEntry(sym.name, sym) - - private def enterInHash(e: ScopeEntry)(implicit ctx: Context): Unit = { - val idx = e.name.hashCode & (hashTable.length - 1) - e.tail = hashTable(idx) - assert(e.tail != e) - hashTable(idx) = e - } - - /** enter a symbol in this scope. */ - final def enter[T <: Symbol](sym: T)(implicit ctx: Context): T = { - if (sym.isType && ctx.phaseId <= ctx.typerPhase.id) { - assert(lookup(sym.name) == NoSymbol, - s"duplicate ${sym.debugString}; previous was ${lookup(sym.name).debugString}") // !!! DEBUG - } - newScopeEntry(sym) - sym - } - - /** enter a symbol, asserting that no symbol with same name exists in scope */ - final def enterUnique(sym: Symbol)(implicit ctx: Context): Unit = { - assert(lookup(sym.name) == NoSymbol, (sym.showLocated, lookup(sym.name).showLocated)) - enter(sym) - } - - private def ensureCapacity(tableSize: Int)(implicit ctx: Context): Unit = - if (size >= tableSize * FillFactor) createHash(tableSize * 2) - - private def createHash(tableSize: Int)(implicit ctx: Context): Unit = - if (size > tableSize * FillFactor) createHash(tableSize * 2) - else { - hashTable = new Array[ScopeEntry](tableSize) - enterAllInHash(lastEntry) - // checkConsistent() // DEBUG - } - - private def enterAllInHash(e: ScopeEntry, n: Int = 0)(implicit ctx: Context): Unit = { - if (e ne null) { - if (n < MaxRecursions) { - enterAllInHash(e.prev, n + 1) - enterInHash(e) - } else { - var entries: List[ScopeEntry] = List() - var ee = e - while (ee ne null) { - entries = ee :: entries - ee = ee.prev - } - entries foreach enterInHash - } - } - } - - /** Remove entry from this scope (which is required to be present) */ - final def unlink(e: ScopeEntry)(implicit ctx: Context): Unit = { - if (lastEntry == e) { - lastEntry = e.prev - } else { - var e1 = lastEntry - while (e1.prev != e) e1 = e1.prev - e1.prev = e.prev - } - if (hashTable ne null) { - val index = e.name.hashCode & (hashTable.length - 1) - var e1 = hashTable(index) - if (e1 == e) - hashTable(index) = e.tail - else { - while (e1.tail != e) e1 = e1.tail - e1.tail = e.tail - } - } - elemsCache = null - size -= 1 - } - - /** remove symbol from this scope if it is present */ - final def unlink(sym: Symbol)(implicit ctx: Context): Unit = { - var e = lookupEntry(sym.name) - while (e ne null) { - if (e.sym == sym) unlink(e) - e = lookupNextEntry(e) - } - } - - /** Replace symbol `prev` (if it exists in current scope) by symbol `replacement`. - * @pre `prev` and `replacement` have the same name. - */ - final def replace(prev: Symbol, replacement: Symbol)(implicit ctx: Context): Unit = { - require(prev.name == replacement.name) - var e = lookupEntry(prev.name) - while (e ne null) { - if (e.sym == prev) e.sym = replacement - e = lookupNextEntry(e) - } - elemsCache = null - } - - /** Lookup a symbol entry matching given name. - */ - override final def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { - var e: ScopeEntry = null - if (hashTable ne null) { - e = hashTable(name.hashCode & (hashTable.length - 1)) - while ((e ne null) && e.name != name) { - e = e.tail - } - } else { - e = lastEntry - while ((e ne null) && e.name != name) { - e = e.prev - } - } - e - } - - /** lookup next entry with same name as this one */ - override final def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry = { - var e = entry - if (hashTable ne null) - do { e = e.tail } while ((e ne null) && e.name != entry.name) - else - do { e = e.prev } while ((e ne null) && e.name != entry.name) - e - } - - /** Returns all symbols as a list in the order they were entered in this scope. - * Does _not_ include the elements of inherited scopes. - */ - override final def toList: List[Symbol] = { - if (elemsCache eq null) { - elemsCache = Nil - var e = lastEntry - while ((e ne null) && e.owner == this) { - elemsCache = e.sym :: elemsCache - e = e.prev - } - } - elemsCache - } - - override def implicitDecls(implicit ctx: Context): List[TermRef] = { - var irefs = new mutable.ListBuffer[TermRef] - var e = lastEntry - while (e ne null) { - if (e.sym is Implicit) { - val d = e.sym.denot - irefs += TermRef.withSigAndDenot(NoPrefix, d.name.asTermName, d.signature, d) - } - e = e.prev - } - irefs.toList - } - - /** Vanilla scope - symbols are stored in declaration order. - */ - final def sorted: List[Symbol] = toList - - override def foreach[U](p: Symbol => U): Unit = toList foreach p - - override def filter(p: Symbol => Boolean): List[Symbol] = { - var syms: List[Symbol] = Nil - var e = lastEntry - while ((e ne null) && e.owner == this) { - val sym = e.sym - if (p(sym)) syms = sym :: syms - e = e.prev - } - syms - } - - override def openForMutations: MutableScope = this - - /** Check that all symbols in this scope are in their correct hashtable buckets. */ - override def checkConsistent()(implicit ctx: Context) = { - var e = lastEntry - while (e != null) { - var e1 = lookupEntry(e.name) - while (e1 != e && e1 != null) e1 = lookupNextEntry(e1) - assert(e1 == e, s"PANIC: Entry ${e.name} is badly linked") - e = e.prev - } - } - } - - /** Create a new scope */ - def newScope: MutableScope = new MutableScope() - - /** Create a new scope nested in another one with which it shares its elements */ - def newNestedScope(outer: Scope)(implicit ctx: Context): MutableScope = new MutableScope(outer) - - /** Create a new scope with given initial elements */ - def newScopeWith(elems: Symbol*)(implicit ctx: Context): MutableScope = { - val scope = newScope - elems foreach scope.enter - scope - } - - /** Create new scope for the members of package `pkg` */ - def newPackageScope(pkgClass: Symbol): MutableScope = newScope - - /** Transform scope of members of `owner` using operation `op` - * This is overridden by the reflective compiler to avoid creating new scopes for packages - */ - def scopeTransform(owner: Symbol)(op: => MutableScope): MutableScope = op - - val selectAll: SymDenotation => Boolean = alwaysTrue - val selectPrivate: SymDenotation => Boolean = d => (d.flagsUNSAFE is Flags.Private) - val selectNonPrivate: SymDenotation => Boolean = d => !(d.flagsUNSAFE is Flags.Private) - - /** The empty scope (immutable). - */ - object EmptyScope extends Scope { - override private[dotc] def lastEntry = null - override def size = 0 - override def nestingLevel = 0 - override def toList = Nil - override def cloneScope(implicit ctx: Context): MutableScope = unsupported("cloneScope") - override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = null - override def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry = null - } - - /** A class for error scopes (mutable) - */ - class ErrorScope(owner: Symbol) extends MutableScope -} diff --git a/src/dotty/tools/dotc/core/Signature.scala b/src/dotty/tools/dotc/core/Signature.scala deleted file mode 100644 index b2e627cbe..000000000 --- a/src/dotty/tools/dotc/core/Signature.scala +++ /dev/null @@ -1,103 +0,0 @@ -package dotty.tools.dotc -package core - -import Names._, Types._, Contexts._, StdNames._ -import TypeErasure.sigName - -/** The signature of a denotation. - * Overloaded denotations with the same name are distinguished by - * their signatures. A signature of a method (of type PolyType,MethodType, or ExprType) is - * composed of a list of signature names, one for each parameter type, plus a signature for - * the result type. Methods are uncurried before taking their signatures. - * The signature name of a type is the fully qualified name of the type symbol of the type's erasure. - * - * For instance a definition - * - * def f(x: Int)(y: List[String]): String - * - * would have signature - * - * Signature( - * List("scala.Int".toTypeName, "scala.collection.immutable.List".toTypeName), - * "scala.String".toTypeName) - * - * The signatures of non-method types are always `NotAMethod`. - * - * There are three kinds of "missing" parts of signatures: - * - * - tpnme.EMPTY Result type marker for NotAMethod and OverloadedSignature - * - tpnme.WILDCARD Arises from a Wildcard or error type - * - tpnme.Uninstantiated Arises from an uninstantiated type variable - */ -case class Signature(paramsSig: List[TypeName], resSig: TypeName) { - import Signature._ - - /** Two names are consistent if they are the same or one of them is tpnme.Uninstantiated */ - private def consistent(name1: TypeName, name2: TypeName) = - name1 == name2 || name1 == tpnme.Uninstantiated || name2 == tpnme.Uninstantiated - - /** Does this signature coincide with that signature on their parameter parts? - * This is the case if all parameter names are _consistent_, i.e. they are either - * equal or on of them is tpnme.Uninstantiated. - */ - final def consistentParams(that: Signature): Boolean = { - def loop(names1: List[TypeName], names2: List[TypeName]): Boolean = - if (names1.isEmpty) names2.isEmpty - else names2.nonEmpty && consistent(names1.head, names2.head) && loop(names1.tail, names2.tail) - loop(this.paramsSig, that.paramsSig) - } - - /** The degree to which this signature matches `that`. - * If parameter names are consistent and result types names match (i.e. they are the same - * or one is a wildcard), the result is `FullMatch`. - * If only the parameter names are consistent, the result is `ParamMatch` before erasure and - * `NoMatch` otherwise. - * If the parameters are inconsistent, the result is always `NoMatch`. - */ - final def matchDegree(that: Signature)(implicit ctx: Context): MatchDegree = - if (consistentParams(that)) - if (resSig == that.resSig || isWildcard(resSig) || isWildcard(that.resSig)) FullMatch - else if (!ctx.erasedTypes) ParamMatch - else NoMatch - else NoMatch - - /** name.toString == "" or name.toString == "_" */ - private def isWildcard(name: TypeName) = name.isEmpty || name == tpnme.WILDCARD - - /** Construct a signature by prepending the signature names of the given `params` - * to the parameter part of this signature. - */ - def prepend(params: List[Type], isJava: Boolean)(implicit ctx: Context) = - Signature((params.map(sigName(_, isJava))) ++ paramsSig, resSig) - - /** A signature is under-defined if its paramsSig part contains at least one - * `tpnme.Uninstantiated`. Under-defined signatures arise when taking a signature - * of a type that still contains uninstantiated type variables. They are eliminated - * by `fixSignature` in `PostTyper`. - */ - def isUnderDefined(implicit ctx: Context) = - paramsSig.contains(tpnme.Uninstantiated) || resSig == tpnme.Uninstantiated -} - -object Signature { - - type MatchDegree = Int - val NoMatch = 0 - val ParamMatch = 1 - val FullMatch = 2 - - /** The signature of everything that's not a method, i.e. that has - * a type different from PolyType, MethodType, or ExprType. - */ - val NotAMethod = Signature(List(), EmptyTypeName) - - /** The signature of an overloaded denotation. - */ - val OverloadedSignature = Signature(List(tpnme.OVERLOADED), EmptyTypeName) - - /** The signature of a method with no parameters and result type `resultType`. */ - def apply(resultType: Type, isJava: Boolean)(implicit ctx: Context): Signature = { - assert(!resultType.isInstanceOf[ExprType]) - apply(Nil, sigName(resultType, isJava)) - } -} diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala deleted file mode 100644 index c2a14b36f..000000000 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ /dev/null @@ -1,844 +0,0 @@ -package dotty.tools.dotc -package core - -import scala.language.implicitConversions -import scala.collection.{mutable, immutable} -import scala.annotation.switch -import Names._ -import Symbols._ -import Contexts._ -import Decorators.StringDecorator -import util.NameTransformer -import scala.collection.breakOut - -object StdNames { - -/** Base strings from which synthetic names are derived. */ - - abstract class DefinedNames[N <: Name] { - protected implicit def fromString(s: String): N - protected def fromName(name: Name): N = fromString(name.toString) - - private val kws = mutable.Set[N]() - protected def kw(name: N) = { kws += name; name } - - final val keywords: collection.Set[N] = kws - } - - abstract class ScalaNames[N <: Name] extends DefinedNames[N] { - protected def encode(s: String): N = fromName(fromString(s).encode) - -// Keywords, need to come first ----------------------- - - final val ABSTRACTkw: N = kw("abstract") - final val CASEkw: N = kw("case") - final val CLASSkw: N = kw("class") - final val CATCHkw: N = kw("catch") - final val DEFkw: N = kw("def") - final val DOkw: N = kw("do") - final val ELSEkw: N = kw("else") - final val EXTENDSkw: N = kw("extends") - final val FALSEkw: N = kw("false") - final val FINALkw: N = kw("final") - final val FINALLYkw: N = kw("finally") - final val FORkw: N = kw("for") - final val FORSOMEkw: N = kw("forSome") - final val IFkw: N = kw("if") - final val IMPLICITkw: N = kw("implicit") - final val IMPORTkw: N = kw("import") - final val INLINEkw: N = kw("inline") - final val LAZYkw: N = kw("lazy") - final val MACROkw: N = kw("macro") - final val MATCHkw: N = kw("match") - final val NEWkw: N = kw("new") - final val NULLkw: N = kw("null") - final val OBJECTkw: N = kw("object") - final val OVERRIDEkw: N = kw("override") - final val PACKAGEkw: N = kw("package") - final val PRIVATEkw: N = kw("private") - final val PROTECTEDkw: N = kw("protected") - final val RETURNkw: N = kw("return") - final val SEALEDkw: N = kw("sealed") - final val SUPERkw: N = kw("super") - final val THENkw: N = kw("then") - final val THISkw: N = kw("this") - final val THROWkw: N = kw("throw") - final val TRAITkw: N = kw("trait") - final val TRUEkw: N = kw("true") - final val TRYkw: N = kw("try") - final val TYPEkw: N = kw("type") - final val VALkw: N = kw("val") - final val VARkw: N = kw("var") - final val WITHkw: N = kw("with") - final val WHILEkw: N = kw("while") - final val YIELDkw: N = kw("yield") - final val DOTkw: N = kw(".") - final val USCOREkw: N = kw("_") - final val COLONkw: N = kw(":") - final val EQUALSkw: N = kw("=") - final val ARROWkw: N = kw("=>") - final val LARROWkw: N = kw("<-") - final val SUBTYPEkw: N = kw("<:") - final val VIEWBOUNDkw: N = kw("<%") - final val SUPERTYPEkw: N = kw(">:") - final val HASHkw: N = kw("#") - final val ATkw: N = kw("@") - - val ANON_CLASS: N = "$anon" - val ANON_FUN: N = "$anonfun" - val BITMAP_PREFIX: N = "bitmap$" - val BITMAP_NORMAL: N = BITMAP_PREFIX // initialization bitmap for public/protected lazy vals - val BITMAP_TRANSIENT: N = BITMAP_PREFIX + "trans$" // initialization bitmap for transient lazy vals - val BITMAP_CHECKINIT: N = BITMAP_PREFIX + "init$" // initialization bitmap for checkinit values - val BITMAP_CHECKINIT_TRANSIENT: N = BITMAP_PREFIX + "inittrans$" // initialization bitmap for transient checkinit values - val DEFAULT_GETTER: N = "$default$" - val DEFAULT_GETTER_INIT: N = NameTransformer.encode("<init>") - val DO_WHILE_PREFIX: N = "doWhile$" - val EMPTY: N = "" - val EMPTY_PACKAGE: N = Names.EMPTY_PACKAGE.toString - val EVIDENCE_PARAM_PREFIX: N = "evidence$" - val EXCEPTION_RESULT_PREFIX: N = "exceptionResult" - val EXPAND_SEPARATOR: N = "$$" - val IMPL_CLASS_SUFFIX: N = "$class" - val IMPORT: N = "<import>" - val INLINE_ACCESSOR_PREFIX = "$inlineAccessor$" - val INTERPRETER_IMPORT_WRAPPER: N = "$iw" - val INTERPRETER_LINE_PREFIX: N = "line" - val INTERPRETER_VAR_PREFIX: N = "res" - val INTERPRETER_WRAPPER_SUFFIX: N = "$object" - val LOCALDUMMY_PREFIX: N = "<local " // owner of local blocks - val MODULE_SUFFIX: N = NameTransformer.MODULE_SUFFIX_STRING - val AVOID_CLASH_SUFFIX: N = "$_avoid_name_clash_$" - val MODULE_VAR_SUFFIX: N = "$module" - val NAME_JOIN: N = NameTransformer.NAME_JOIN_STRING - val USCORE_PARAM_PREFIX: N = "_$" - val OPS_PACKAGE: N = "<special-ops>" - val OVERLOADED: N = "<overloaded>" - val PACKAGE: N = "package" - val PACKAGE_CLS: N = "package$" - val PROTECTED_PREFIX: N = "protected$" - val PROTECTED_SET_PREFIX: N = PROTECTED_PREFIX + "set" - val ROOT: N = "<root>" - val SHADOWED: N = "(shadowed)" // tag to be used until we have proper name kinds - val SINGLETON_SUFFIX: N = ".type" - val SPECIALIZED_SUFFIX: N = "$sp" - val SUPER_PREFIX: N = "super$" - val WHILE_PREFIX: N = "while$" - val DEFAULT_EXCEPTION_NAME: N = "ex$" - val INITIALIZER_PREFIX: N = "initial$" - val COMPANION_MODULE_METHOD: N = "companion$module" - val COMPANION_CLASS_METHOD: N = "companion$class" - val TRAIT_SETTER_SEPARATOR: N = "$_setter_$" - - // value types (and AnyRef) are all used as terms as well - // as (at least) arguments to the @specialize annotation. - final val Boolean: N = "Boolean" - final val Byte: N = "Byte" - final val Char: N = "Char" - final val Double: N = "Double" - final val Float: N = "Float" - final val Int: N = "Int" - final val Long: N = "Long" - final val Short: N = "Short" - final val Unit: N = "Unit" - - final val ScalaValueNames: scala.List[N] = - scala.List(Byte, Char, Short, Int, Long, Float, Double, Boolean, Unit) - - // some types whose companions we utilize - final val AnyRef: N = "AnyRef" - final val Array: N = "Array" - final val List: N = "List" - final val Seq: N = "Seq" - final val Symbol: N = "Symbol" - final val ClassTag: N = "ClassTag" - final val classTag: N = "classTag" - final val WeakTypeTag: N = "WeakTypeTag" - final val TypeTag : N = "TypeTag" - final val typeTag: N = "typeTag" - final val Expr: N = "Expr" - final val String: N = "String" - final val Annotation: N = "Annotation" - - // fictions we use as both types and terms - final val ERROR: N = "<error>" - final val ERRORenc: N = encode("<error>") - final val NO_NAME: N = "<none>" // formerly NOSYMBOL - final val WILDCARD: N = "_" - -// ----- Type names ----------------------------------------- - - final val BYNAME_PARAM_CLASS: N = "<byname>" - final val EQUALS_PATTERN: N = "<equals>" - final val LOCAL_CHILD: N = "<local child>" - final val REPEATED_PARAM_CLASS: N = "<repeated>" - final val WILDCARD_STAR: N = "_*" - final val REIFY_TREECREATOR_PREFIX: N = "$treecreator" - final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator" - - final val AbstractFunction: N = "AbstractFunction" - final val Any: N = "Any" - final val AnyVal: N = "AnyVal" - final val ExprApi: N = "ExprApi" - final val Function: N = "Function" - final val Mirror: N = "Mirror" - final val Nothing: N = "Nothing" - final val Null: N = "Null" - final val Object: N = "Object" - final val PartialFunction: N = "PartialFunction" - final val PrefixType: N = "PrefixType" - final val Product: N = "Product" - final val Serializable: N = "Serializable" - final val Singleton: N = "Singleton" - final val Throwable: N = "Throwable" - final val Tuple: N = "Tuple" - - final val ClassfileAnnotation: N = "ClassfileAnnotation" - final val ClassManifest: N = "ClassManifest" - final val Enum: N = "Enum" - final val Group: N = "Group" - final val Tree: N = "Tree" - final val Type : N = "Type" - final val TypeTree: N = "TypeTree" - - // Annotation simple names, used in Namer - final val BeanPropertyAnnot: N = "BeanProperty" - final val BooleanBeanPropertyAnnot: N = "BooleanBeanProperty" - final val bridgeAnnot: N = "bridge" - - // Classfile Attributes - final val AnnotationDefaultATTR: N = "AnnotationDefault" - final val BridgeATTR: N = "Bridge" - final val ClassfileAnnotationATTR: N = "RuntimeInvisibleAnnotations" // RetentionPolicy.CLASS. Currently not used (Apr 2009). - final val CodeATTR: N = "Code" - final val ConstantValueATTR: N = "ConstantValue" - final val DeprecatedATTR: N = "Deprecated" - final val ExceptionsATTR: N = "Exceptions" - final val InnerClassesATTR: N = "InnerClasses" - final val LineNumberTableATTR: N = "LineNumberTable" - final val LocalVariableTableATTR: N = "LocalVariableTable" - final val RuntimeAnnotationATTR: N = "RuntimeVisibleAnnotations" // RetentionPolicy.RUNTIME - final val RuntimeParamAnnotationATTR: N = "RuntimeVisibleParameterAnnotations" // RetentionPolicy.RUNTIME (annotations on parameters) - final val ScalaATTR: N = "Scala" - final val ScalaSignatureATTR: N = "ScalaSig" - final val TASTYATTR: N = "TASTY" - final val SignatureATTR: N = "Signature" - final val SourceFileATTR: N = "SourceFile" - final val SyntheticATTR: N = "Synthetic" - -// ----- Term names ----------------------------------------- - - // Compiler-internal - val ANYname: N = "<anyname>" - val CONSTRUCTOR: N = Names.CONSTRUCTOR.toString - val DEFAULT_CASE: N = "defaultCase$" - val EVT2U: N = "evt2u$" - val EQEQ_LOCAL_VAR: N = "eqEqTemp$" - val FAKE_LOCAL_THIS: N = "this$" - val LAZY_LOCAL: N = "$lzy" - val LAZY_LOCAL_INIT: N = "$lzyINIT" - val LAZY_FIELD_OFFSET: N = "OFFSET$" - val LAZY_SLOW_SUFFIX: N = "$lzycompute" - val LOCAL_SUFFIX: N = "$$local" - val UNIVERSE_BUILD_PREFIX: N = "$u.build." - val UNIVERSE_BUILD: N = "$u.build" - val UNIVERSE_PREFIX: N = "$u." - val UNIVERSE_SHORT: N = "$u" - val MIRROR_PREFIX: N = "$m." - val MIRROR_SHORT: N = "$m" - val MIRROR_UNTYPED: N = "$m$untyped" - val REIFY_FREE_PREFIX: N = "free$" - val REIFY_FREE_THIS_SUFFIX: N = "$this" - val REIFY_FREE_VALUE_SUFFIX: N = "$value" - val REIFY_SYMDEF_PREFIX: N = "symdef$" - val MODULE_INSTANCE_FIELD: N = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" - val OUTER: N = "$outer" - val OUTER_LOCAL: N = "$outer " - val OUTER_SELECT: N = "_<outer>" // emitted by inliner, replaced by outer path in explicitouter - val REFINE_CLASS: N = "<refinement>" - val ROOTPKG: N = "_root_" - val SELECTOR_DUMMY: N = "<unapply-selector>" - val SELF: N = "$this" - val SETTER_SUFFIX: N = encode("_=") - val SKOLEM: N = "<skolem>" - val SPECIALIZED_INSTANCE: N = "specInstance$" - val THIS: N = "_$this" - val TRAIT_CONSTRUCTOR: N = "$init$" - val U2EVT: N = "u2evt$" - - final val Nil: N = "Nil" - final val Predef: N = "Predef" - final val ScalaRunTime: N = "ScalaRunTime" - final val Some: N = "Some" - - val x_0 : N = "x$0" - val x_1 : N = "x$1" - val x_2 : N = "x$2" - val x_3 : N = "x$3" - val x_4 : N = "x$4" - val x_5 : N = "x$5" - val x_6 : N = "x$6" - val x_7 : N = "x$7" - val x_8 : N = "x$8" - val x_9 : N = "x$9" - val _1 : N = "_1" - val _2 : N = "_2" - val _3 : N = "_3" - val _4 : N = "_4" - val _5 : N = "_5" - val _6 : N = "_6" - val _7 : N = "_7" - val _8 : N = "_8" - val _9 : N = "_9" - val _10 : N = "_10" - val _11 : N = "_11" - val _12 : N = "_12" - val _13 : N = "_13" - val _14 : N = "_14" - val _15 : N = "_15" - val _16 : N = "_16" - val _17 : N = "_17" - val _18 : N = "_18" - val _19 : N = "_19" - val _20 : N = "_20" - val _21 : N = "_21" - val _22 : N = "_22" - - val ??? = encode("???") - - val genericWrapArray: N = "genericWrapArray" - def wrapRefArray: N = "wrapRefArray" - def wrapXArray(clsName: Name): N = "wrap" + clsName + "Array" - - // Compiler utilized names - - val AnnotatedType: N = "AnnotatedType" - val AppliedTypeTree: N = "AppliedTypeTree" - val ArrayAnnotArg: N = "ArrayAnnotArg" - val Constant: N = "Constant" - val ConstantType: N = "ConstantType" - val ExistentialTypeTree: N = "ExistentialTypeTree" - val Flag : N = "Flag" - val Ident: N = "Ident" - val Import: N = "Import" - val Literal: N = "Literal" - val LiteralAnnotArg: N = "LiteralAnnotArg" - val Modifiers: N = "Modifiers" - val NestedAnnotArg: N = "NestedAnnotArg" - val NoFlags: N = "NoFlags" - val NoPrefix: N = "NoPrefix" - val NoSymbol: N = "NoSymbol" - val NoType: N = "NoType" - val Pair: N = "Pair" - val Ref: N = "Ref" - val RootPackage: N = "RootPackage" - val RootClass: N = "RootClass" - val Scala2: N = "Scala2" - val Select: N = "Select" - val StringContext: N = "StringContext" - val This: N = "This" - val ThisType: N = "ThisType" - val Tuple2: N = "Tuple2" - val TYPE_ : N = "TYPE" - val TypeApply: N = "TypeApply" - val TypeRef: N = "TypeRef" - val UNIT : N = "UNIT" - val add_ : N = "add" - val annotation: N = "annotation" - val anyValClass: N = "anyValClass" - val append: N = "append" - val apply: N = "apply" - val applyDynamic: N = "applyDynamic" - val applyDynamicNamed: N = "applyDynamicNamed" - val applyOrElse: N = "applyOrElse" - val args : N = "args" - val argv : N = "argv" - val arrayClass: N = "arrayClass" - val arrayElementClass: N = "arrayElementClass" - val arrayValue: N = "arrayValue" - val array_apply : N = "array_apply" - val array_clone : N = "array_clone" - val array_length : N = "array_length" - val array_update : N = "array_update" - val arraycopy: N = "arraycopy" - val asTerm: N = "asTerm" - val asModule: N = "asModule" - val asMethod: N = "asMethod" - val asType: N = "asType" - val asClass: N = "asClass" - val asInstanceOf_ : N = "asInstanceOf" - val assert_ : N = "assert" - val assume_ : N = "assume" - val box: N = "box" - val build : N = "build" - val bytes: N = "bytes" - val canEqual_ : N = "canEqual" - val checkInitialized: N = "checkInitialized" - val ClassManifestFactory: N = "ClassManifestFactory" - val classOf: N = "classOf" - val clone_ : N = "clone" - // val conforms : N = "conforms" // Dotty deviation: no special treatment of conforms, so the occurrence of the name here would cause to unintended implicit shadowing. Should find a less common name for it in Predef. - val copy: N = "copy" - val currentMirror: N = "currentMirror" - val create: N = "create" - val definitions: N = "definitions" - val delayedInit: N = "delayedInit" - val delayedInitArg: N = "delayedInit$body" - val drop: N = "drop" - val dynamics: N = "dynamics" - val dummyApply: N = "<dummy-apply>" - val elem: N = "elem" - val emptyValDef: N = "emptyValDef" - val ensureAccessible : N = "ensureAccessible" - val eq: N = "eq" - val equalsNumChar : N = "equalsNumChar" - val equalsNumNum : N = "equalsNumNum" - val equalsNumObject : N = "equalsNumObject" - val equals_ : N = "equals" - val error: N = "error" - val eval: N = "eval" - val eqAny: N = "eqAny" - val ex: N = "ex" - val experimental: N = "experimental" - val f: N = "f" - val false_ : N = "false" - val filter: N = "filter" - val finalize_ : N = "finalize" - val find_ : N = "find" - val flagsFromBits : N = "flagsFromBits" - val flatMap: N = "flatMap" - val foreach: N = "foreach" - val genericArrayOps: N = "genericArrayOps" - val get: N = "get" - val getClass_ : N = "getClass" - val getOrElse: N = "getOrElse" - val hasNext: N = "hasNext" - val hashCode_ : N = "hashCode" - val hash_ : N = "hash" - val head: N = "head" - val higherKinds: N = "higherKinds" - val identity: N = "identity" - val implicitly: N = "implicitly" - val in: N = "in" - val info: N = "info" - val inlinedEquals: N = "inlinedEquals" - val isArray: N = "isArray" - val isDefined: N = "isDefined" - val isDefinedAt: N = "isDefinedAt" - val isDefinedAtImpl: N = "$isDefinedAt" - val isEmpty: N = "isEmpty" - val isInstanceOf_ : N = "isInstanceOf" - val java: N = "java" - val key: N = "key" - val lang: N = "lang" - val length: N = "length" - val lengthCompare: N = "lengthCompare" - val liftedTree: N = "liftedTree" - val `macro` : N = "macro" - val macroThis : N = "_this" - val macroContext : N = "c" - val main: N = "main" - val manifest: N = "manifest" - val ManifestFactory: N = "ManifestFactory" - val manifestToTypeTag: N = "manifestToTypeTag" - val map: N = "map" - val materializeClassTag: N = "materializeClassTag" - val materializeWeakTypeTag: N = "materializeWeakTypeTag" - val materializeTypeTag: N = "materializeTypeTag" - val mirror : N = "mirror" - val moduleClass : N = "moduleClass" - val name: N = "name" - val ne: N = "ne" - val newFreeTerm: N = "newFreeTerm" - val newFreeType: N = "newFreeType" - val newNestedSymbol: N = "newNestedSymbol" - val newScopeWith: N = "newScopeWith" - val next: N = "next" - val nmeNewTermName: N = "newTermName" - val nmeNewTypeName: N = "newTypeName" - val noAutoTupling: N = "noAutoTupling" - val normalize: N = "normalize" - val notifyAll_ : N = "notifyAll" - val notify_ : N = "notify" - val null_ : N = "null" - val ofDim: N = "ofDim" - val origin: N = "origin" - val prefix : N = "prefix" - val productArity: N = "productArity" - val productElement: N = "productElement" - val productIterator: N = "productIterator" - val productPrefix: N = "productPrefix" - val readResolve: N = "readResolve" - val reflect : N = "reflect" - val reify : N = "reify" - val rootMirror : N = "rootMirror" - val runOrElse: N = "runOrElse" - val runtime: N = "runtime" - val runtimeClass: N = "runtimeClass" - val runtimeMirror: N = "runtimeMirror" - val sameElements: N = "sameElements" - val scala_ : N = "scala" - val selectDynamic: N = "selectDynamic" - val selectOverloadedMethod: N = "selectOverloadedMethod" - val selectTerm: N = "selectTerm" - val selectType: N = "selectType" - val self: N = "self" - val seqToArray: N = "seqToArray" - val setAccessible: N = "setAccessible" - val setAnnotations: N = "setAnnotations" - val setSymbol: N = "setSymbol" - val setType: N = "setType" - val setTypeSignature: N = "setTypeSignature" - val splice: N = "splice" - val staticClass : N = "staticClass" - val staticModule : N = "staticModule" - val staticPackage : N = "staticPackage" - val synchronized_ : N = "synchronized" - val tail: N = "tail" - val `then` : N = "then" - val this_ : N = "this" - val thisPrefix : N = "thisPrefix" - val throw_ : N = "throw" - val toArray: N = "toArray" - val toList: N = "toList" - val toObjectArray : N = "toObjectArray" - val toSeq: N = "toSeq" - val toString_ : N = "toString" - val toTypeConstructor: N = "toTypeConstructor" - val tpe : N = "tpe" - val tree : N = "tree" - val true_ : N = "true" - val typedProductIterator: N = "typedProductIterator" - val typeTagToManifest: N = "typeTagToManifest" - val unapply: N = "unapply" - val unapplySeq: N = "unapplySeq" - val unbox: N = "unbox" - val universe: N = "universe" - val update: N = "update" - val updateDynamic: N = "updateDynamic" - val value: N = "value" - val valueOf : N = "valueOf" - val values : N = "values" - val view_ : N = "view" - val wait_ : N = "wait" - val withFilter: N = "withFilter" - val withFilterIfRefutable: N = "withFilterIfRefutable$" - val wrap: N = "wrap" - val zero: N = "zero" - val zip: N = "zip" - val nothingRuntimeClass: N = "scala.runtime.Nothing$" - val nullRuntimeClass: N = "scala.runtime.Null$" - - val synthSwitch: N = "$synthSwitch" - - // unencoded operators - object raw { - final val AMP : N = "&" - final val BANG : N = "!" - final val BAR : N = "|" - final val DOLLAR: N = "$" - final val GE: N = ">=" - final val LE: N = "<=" - final val MINUS: N = "-" - final val NE: N = "!=" - final val PLUS : N = "+" - final val SLASH: N = "/" - final val STAR : N = "*" - final val TILDE: N = "~" - - final val isUnary: Set[Name] = Set(MINUS, PLUS, TILDE, BANG) - } - - object specializedTypeNames { - final val Boolean: N = "Z" - final val Byte: N = "B" - final val Char: N = "C" - final val Short: N = "S" - final val Int: N = "I" - final val Long: N = "J" - final val Float: N = "F" - final val Double: N = "D" - final val Void: N = "V" - final val Object: N = "L" - - final val prefix: N = "$m" - final val separator: N = "c" - final val suffix: N = "$sp" - } - - // value-conversion methods - val toByte: N = "toByte" - val toShort: N = "toShort" - val toChar: N = "toChar" - val toInt: N = "toInt" - val toLong: N = "toLong" - val toFloat: N = "toFloat" - val toDouble: N = "toDouble" - - // primitive operation methods for structural types mostly - // overlap with the above, but not for these two. - val toCharacter: N = "toCharacter" - val toInteger: N = "toInteger" - - def newLazyValSlowComputeName(lzyValName: N) = lzyValName ++ LAZY_SLOW_SUFFIX - - // ASCII names for operators - val ADD = encode("+") - val AND = encode("&") - val ASR = encode(">>") - val DIV = encode("/") - val EQ = encode("==") - val EQL = encode("=") - val GE = encode(">=") - val GT = encode(">") - val HASHHASH = encode("##") - val LE = encode("<=") - val LSL = encode("<<") - val LSR = encode(">>>") - val LT = encode("<") - val MINUS = encode("-") - val MOD = encode("%") - val MUL = encode("*") - val NE = encode("!=") - val OR = encode("|") - val PLUS = ADD // technically redundant, but ADD looks funny with MINUS - val SUB = MINUS // ... as does SUB with PLUS - val XOR = encode("^") - val ZAND = encode("&&") - val ZOR = encode("||") - - // unary operators - val UNARY_PREFIX: N = "unary_" - val UNARY_~ = encode("unary_~") - val UNARY_+ = encode("unary_+") - val UNARY_- = encode("unary_-") - val UNARY_! = encode("unary_!") - - // Grouped here so Cleanup knows what tests to perform. - val CommonOpNames = Set[Name](OR, XOR, AND, EQ, NE) - val ConversionNames = Set[Name](toByte, toChar, toDouble, toFloat, toInt, toLong, toShort) - val BooleanOpNames = Set[Name](ZOR, ZAND, UNARY_!) ++ CommonOpNames - val NumberOpNames = ( - Set[Name](ADD, SUB, MUL, DIV, MOD, LSL, LSR, ASR, LT, LE, GE, GT) - ++ Set(UNARY_+, UNARY_-, UNARY_!) - ++ ConversionNames - ++ CommonOpNames - ) - - val add: N = "add" - val complement: N = "complement" - val divide: N = "divide" - val multiply: N = "multiply" - val negate: N = "negate" - val positive: N = "positive" - val shiftLogicalRight: N = "shiftLogicalRight" - val shiftSignedLeft: N = "shiftSignedLeft" - val shiftSignedRight: N = "shiftSignedRight" - val subtract: N = "subtract" - val takeAnd: N = "takeAnd" - val takeConditionalAnd: N = "takeConditionalAnd" - val takeConditionalOr: N = "takeConditionalOr" - val takeModulo: N = "takeModulo" - val takeNot: N = "takeNot" - val takeOr: N = "takeOr" - val takeXor: N = "takeXor" - val testEqual: N = "testEqual" - val testGreaterOrEqualThan: N = "testGreaterOrEqualThan" - val testGreaterThan: N = "testGreaterThan" - val testLessOrEqualThan: N = "testLessOrEqualThan" - val testLessThan: N = "testLessThan" - val testNotEqual: N = "testNotEqual" - - val isBoxedNumberOrBoolean: N = "isBoxedNumberOrBoolean" - val isBoxedNumber: N = "isBoxedNumber" - - val reflPolyCacheName: N = "reflPoly$Cache" - val reflClassCacheName: N = "reflClass$Cache" - val reflParamsCacheName: N = "reflParams$Cache" - val reflMethodCacheName: N = "reflMethod$Cache" - val reflMethodName: N = "reflMethod$Method" - - private val reflectionCacheNames = Set[N]( - reflPolyCacheName, - reflClassCacheName, - reflParamsCacheName, - reflMethodCacheName, - reflMethodName - ) - - def isReflectionCacheName(name: Name) = reflectionCacheNames exists (name startsWith _) - } - - class ScalaTermNames extends ScalaNames[TermName] { - protected implicit def fromString(s: String): TermName = termName(s) - - @switch def syntheticParamName(i: Int): TermName = i match { - case 0 => x_0 - case 1 => x_1 - case 2 => x_2 - case 3 => x_3 - case 4 => x_4 - case 5 => x_5 - case 6 => x_6 - case 7 => x_7 - case 8 => x_8 - case 9 => x_9 - case _ => termName("x$" + i) - } - - @switch def productAccessorName(j: Int): TermName = j match { - case 1 => nme._1 - case 2 => nme._2 - case 3 => nme._3 - case 4 => nme._4 - case 5 => nme._5 - case 6 => nme._6 - case 7 => nme._7 - case 8 => nme._8 - case 9 => nme._9 - case 10 => nme._10 - case 11 => nme._11 - case 12 => nme._12 - case 13 => nme._13 - case 14 => nme._14 - case 15 => nme._15 - case 16 => nme._16 - case 17 => nme._17 - case 18 => nme._18 - case 19 => nme._19 - case 20 => nme._20 - case 21 => nme._21 - case 22 => nme._22 - case _ => termName("_" + j) - } - - def syntheticParamNames(num: Int): List[TermName] = - (0 until num).map(syntheticParamName)(breakOut) - - def localDummyName(clazz: Symbol)(implicit ctx: Context): TermName = - LOCALDUMMY_PREFIX ++ clazz.name ++ ">" - - def newBitmapName(bitmapPrefix: TermName, n: Int): TermName = bitmapPrefix ++ n.toString - - def selectorName(n: Int): TermName = "_" + (n + 1) - - object primitive { - val arrayApply: TermName = "[]apply" - val arrayUpdate: TermName = "[]update" - val arrayLength: TermName = "[]length" - val names: Set[Name] = Set(arrayApply, arrayUpdate, arrayLength) - } - - def isPrimitiveName(name: Name) = primitive.names.contains(name) - } - - class ScalaTypeNames extends ScalaNames[TypeName] { - protected implicit def fromString(s: String): TypeName = typeName(s) - - def syntheticTypeParamName(i: Int): TypeName = "X" + i - - def syntheticTypeParamNames(num: Int): List[TypeName] = - (0 until num).map(syntheticTypeParamName)(breakOut) - - final val Conforms = encode("<:<") - - final val Uninstantiated: TypeName = "?$" - } - - abstract class JavaNames[N <: Name] extends DefinedNames[N] { - final val ABSTRACTkw: N = kw("abstract") - final val ASSERTkw: N = kw("assert") - final val BOOLEANkw: N = kw("boolean") - final val BREAKkw: N = kw("break") - final val BYTEkw: N = kw("byte") - final val CASEkw: N = kw("case") - final val CATCHkw: N = kw("catch") - final val CHARkw: N = kw("char") - final val CLASSkw: N = kw("class") - final val CONSTkw: N = kw("const") - final val CONTINUEkw: N = kw("continue") - final val DEFAULTkw: N = kw("default") - final val DOkw: N = kw("do") - final val DOUBLEkw: N = kw("double") - final val ELSEkw: N = kw("else") - final val ENUMkw: N = kw("enum") - final val EXTENDSkw: N = kw("extends") - final val FINALkw: N = kw("final") - final val FINALLYkw: N = kw("finally") - final val FLOATkw: N = kw("float") - final val FORkw: N = kw("for") - final val IFkw: N = kw("if") - final val GOTOkw: N = kw("goto") - final val IMPLEMENTSkw: N = kw("implements") - final val IMPORTkw: N = kw("import") - final val INSTANCEOFkw: N = kw("instanceof") - final val INTkw: N = kw("int") - final val INTERFACEkw: N = kw("interface") - final val LONGkw: N = kw("long") - final val NATIVEkw: N = kw("native") - final val NEWkw: N = kw("new") - final val PACKAGEkw: N = kw("package") - final val PRIVATEkw: N = kw("private") - final val PROTECTEDkw: N = kw("protected") - final val PUBLICkw: N = kw("public") - final val RETURNkw: N = kw("return") - final val SHORTkw: N = kw("short") - final val STATICkw: N = kw("static") - final val STRICTFPkw: N = kw("strictfp") - final val SUPERkw: N = kw("super") - final val SWITCHkw: N = kw("switch") - final val SYNCHRONIZEDkw: N = kw("synchronized") - final val THISkw: N = kw("this") - final val THROWkw: N = kw("throw") - final val THROWSkw: N = kw("throws") - final val TRANSIENTkw: N = kw("transient") - final val TRYkw: N = kw("try") - final val VOIDkw: N = kw("void") - final val VOLATILEkw: N = kw("volatile") - final val WHILEkw: N = kw("while") - - final val BoxedBoolean: N = "java.lang.Boolean" - final val BoxedByte: N = "java.lang.Byte" - final val BoxedCharacter: N = "java.lang.Character" - final val BoxedDouble: N = "java.lang.Double" - final val BoxedFloat: N = "java.lang.Float" - final val BoxedInteger: N = "java.lang.Integer" - final val BoxedLong: N = "java.lang.Long" - final val BoxedNumber: N = "java.lang.Number" - final val BoxedShort: N = "java.lang.Short" - final val Class: N = "java.lang.Class" - final val IOOBException: N = "java.lang.IndexOutOfBoundsException" - final val InvTargetException: N = "java.lang.reflect.InvocationTargetException" - final val MethodAsObject: N = "java.lang.reflect.Method" - final val NPException: N = "java.lang.NullPointerException" - final val Object: N = "java.lang.Object" - final val String: N = "java.lang.String" - final val Throwable: N = "java.lang.Throwable" - - final val ForName: N = "forName" - final val GetCause: N = "getCause" - final val GetClass: N = "getClass" - final val GetClassLoader: N = "getClassLoader" - final val GetComponentType: N = "getComponentType" - final val GetMethod: N = "getMethod" - final val Invoke: N = "invoke" - final val JavaLang: N = "java.lang" - - final val BeanProperty: N = "scala.beans.BeanProperty" - final val BooleanBeanProperty: N = "scala.beans.BooleanBeanProperty" - final val JavaSerializable: N = "java.io.Serializable" - } - - class JavaTermNames extends JavaNames[TermName] { - protected def fromString(s: String): TermName = termName(s) - } - class JavaTypeNames extends JavaNames[TypeName] { - protected def fromString(s: String): TypeName = typeName(s) - } - - val nme = new ScalaTermNames - val tpnme = new ScalaTypeNames - val jnme = new JavaTermNames - val jtpnme = new JavaTypeNames - -} diff --git a/src/dotty/tools/dotc/core/Substituters.scala b/src/dotty/tools/dotc/core/Substituters.scala deleted file mode 100644 index 23683608a..000000000 --- a/src/dotty/tools/dotc/core/Substituters.scala +++ /dev/null @@ -1,306 +0,0 @@ -package dotty.tools.dotc.core - -import Types._, Symbols._, Contexts._, Names._ - -/** Substitution operations on types. See the corresponding `subst` and - * `substThis` methods on class Type for an explanation. - */ -trait Substituters { this: Context => - - final def subst(tp: Type, from: BindingType, to: BindingType, theMap: SubstBindingMap): Type = - tp match { - case tp: BoundType => - if (tp.binder eq from) tp.copyBoundType(to.asInstanceOf[tp.BT]) else tp - case tp: NamedType => - if (tp.currentSymbol.isStatic) tp - else tp.derivedSelect(subst(tp.prefix, from, to, theMap)) - case _: ThisType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(subst(tp.parent, from, to, theMap), tp.refinedName, subst(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(subst(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new SubstBindingMap(from, to)) - .mapOver(tp) - } - - final def subst1(tp: Type, from: Symbol, to: Type, theMap: Subst1Map): Type = { - tp match { - case tp: NamedType => - val sym = tp.symbol - if (sym eq from) return to - if (sym.isStatic && !from.isStatic) tp - else tp.derivedSelect(subst1(tp.prefix, from, to, theMap)) - case _: ThisType | _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(subst1(tp.parent, from, to, theMap), tp.refinedName, subst1(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(subst1(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new Subst1Map(from, to)) - .mapOver(tp) - } - } - - final def subst2(tp: Type, from1: Symbol, to1: Type, from2: Symbol, to2: Type, theMap: Subst2Map): Type = { - tp match { - case tp: NamedType => - val sym = tp.symbol - if (sym eq from1) return to1 - if (sym eq from2) return to2 - if (sym.isStatic && !from1.isStatic && !from2.isStatic) tp - else tp.derivedSelect(subst2(tp.prefix, from1, to1, from2, to2, theMap)) - case _: ThisType | _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(subst2(tp.parent, from1, to1, from2, to2, theMap), tp.refinedName, subst2(tp.refinedInfo, from1, to1, from2, to2, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(subst2(tp.alias, from1, to1, from2, to2, theMap)) - case _ => - (if (theMap != null) theMap else new Subst2Map(from1, to1, from2, to2)) - .mapOver(tp) - } - } - - final def subst(tp: Type, from: List[Symbol], to: List[Type], theMap: SubstMap): Type = { - tp match { - case tp: NamedType => - val sym = tp.symbol - var fs = from - var ts = to - while (fs.nonEmpty) { - if (fs.head eq sym) return ts.head - fs = fs.tail - ts = ts.tail - } - if (sym.isStatic && !existsStatic(from)) tp - else tp.derivedSelect(subst(tp.prefix, from, to, theMap)) - case _: ThisType | _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(subst(tp.parent, from, to, theMap), tp.refinedName, subst(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(subst(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new SubstMap(from, to)) - .mapOver(tp) - } - } - - final def substDealias(tp: Type, from: List[Symbol], to: List[Type], theMap: SubstDealiasMap): Type = { - tp match { - case tp: NamedType => - val sym = tp.symbol - var fs = from - var ts = to - while (fs.nonEmpty) { - if (fs.head eq sym) return ts.head - fs = fs.tail - ts = ts.tail - } - if (sym.isStatic && !existsStatic(from)) tp - else { - tp.info match { - case TypeAlias(alias) => - val alias1 = substDealias(alias, from, to, theMap) - if (alias1 ne alias) return alias1 - case _ => - } - tp.derivedSelect(substDealias(tp.prefix, from, to, theMap)) - } - case _: ThisType | _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(substDealias(tp.parent, from, to, theMap), tp.refinedName, substDealias(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(substDealias(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new SubstDealiasMap(from, to)) - .mapOver(tp) - } - } - - final def substSym(tp: Type, from: List[Symbol], to: List[Symbol], theMap: SubstSymMap): Type = - tp match { - case tp: NamedType => - val sym = tp.symbol - var fs = from - var ts = to - while (fs.nonEmpty) { - if (fs.head eq sym) - return tp match { - case tp: WithFixedSym => NamedType.withFixedSym(tp.prefix, ts.head) - case _ => substSym(tp.prefix, from, to, theMap) select ts.head - } - fs = fs.tail - ts = ts.tail - } - if (sym.isStatic && !existsStatic(from)) tp - else tp.derivedSelect(substSym(tp.prefix, from, to, theMap)) - case tp: ThisType => - val sym = tp.cls - var fs = from - var ts = to - while (fs.nonEmpty) { - if (fs.head eq sym) return ts.head.asClass.thisType - fs = fs.tail - ts = ts.tail - } - tp - case _: ThisType | _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(substSym(tp.parent, from, to, theMap), tp.refinedName, substSym(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(substSym(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new SubstSymMap(from, to)) - .mapOver(tp) - } - - final def substThis(tp: Type, from: ClassSymbol, to: Type, theMap: SubstThisMap): Type = - tp match { - case tp: ThisType => - if (tp.cls eq from) to else tp - case tp: NamedType => - if (tp.currentSymbol.isStaticOwner) tp - else tp.derivedSelect(substThis(tp.prefix, from, to, theMap)) - case _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(substThis(tp.parent, from, to, theMap), tp.refinedName, substThis(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(substThis(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new SubstThisMap(from, to)) - .mapOver(tp) - } - - final def substRecThis(tp: Type, from: Type, to: Type, theMap: SubstRecThisMap): Type = - tp match { - case tp @ RecThis(binder) => - if (binder eq from) to else tp - case tp: NamedType => - if (tp.currentSymbol.isStatic) tp - else tp.derivedSelect(substRecThis(tp.prefix, from, to, theMap)) - case _: ThisType | _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(substRecThis(tp.parent, from, to, theMap), tp.refinedName, substRecThis(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(substRecThis(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new SubstRecThisMap(from, to)) - .mapOver(tp) - } - - final def substParam(tp: Type, from: ParamType, to: Type, theMap: SubstParamMap): Type = - tp match { - case tp: BoundType => - if (tp == from) to else tp - case tp: NamedType => - if (tp.currentSymbol.isStatic) tp - else tp.derivedSelect(substParam(tp.prefix, from, to, theMap)) - case _: ThisType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(substParam(tp.parent, from, to, theMap), tp.refinedName, substParam(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(substParam(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new SubstParamMap(from, to)) - .mapOver(tp) - } - - final def substParams(tp: Type, from: BindingType, to: List[Type], theMap: SubstParamsMap): Type = - tp match { - case tp: ParamType => - if (tp.binder == from) to(tp.paramNum) else tp - case tp: NamedType => - if (tp.currentSymbol.isStatic) tp - else tp.derivedSelect(substParams(tp.prefix, from, to, theMap)) - case _: ThisType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(substParams(tp.parent, from, to, theMap), tp.refinedName, substParams(tp.refinedInfo, from, to, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(substParams(tp.alias, from, to, theMap)) - case _ => - (if (theMap != null) theMap else new SubstParamsMap(from, to)) - .mapOver(tp) - } - - private def existsStatic(syms: List[Symbol]): Boolean = syms match { - case sym :: syms1 => sym.isStatic || existsStatic(syms1) - case nil => false - } - - final class SubstBindingMap(from: BindingType, to: BindingType) extends DeepTypeMap { - def apply(tp: Type) = subst(tp, from, to, this) - } - - final class Subst1Map(from: Symbol, to: Type) extends DeepTypeMap { - def apply(tp: Type) = subst1(tp, from, to, this) - } - - final class Subst2Map(from1: Symbol, to1: Type, from2: Symbol, to2: Type) extends DeepTypeMap { - def apply(tp: Type) = subst2(tp, from1, to1, from2, to2, this) - } - - final class SubstMap(from: List[Symbol], to: List[Type]) extends DeepTypeMap { - def apply(tp: Type): Type = subst(tp, from, to, this) - } - - final class SubstDealiasMap(from: List[Symbol], to: List[Type]) extends DeepTypeMap { - override def apply(tp: Type): Type = substDealias(tp, from, to, this) - } - - final class SubstSymMap(from: List[Symbol], to: List[Symbol]) extends DeepTypeMap { - def apply(tp: Type): Type = substSym(tp, from, to, this) - } - - final class SubstThisMap(from: ClassSymbol, to: Type) extends DeepTypeMap { - def apply(tp: Type): Type = substThis(tp, from, to, this) - } - - final class SubstRecThisMap(from: Type, to: Type) extends DeepTypeMap { - def apply(tp: Type): Type = substRecThis(tp, from, to, this) - } - - final class SubstParamMap(from: ParamType, to: Type) extends DeepTypeMap { - def apply(tp: Type) = substParam(tp, from, to, this) - } - - final class SubstParamsMap(from: BindingType, to: List[Type]) extends DeepTypeMap { - def apply(tp: Type) = substParams(tp, from, to, this) - } - - /** A map for "cycle safe substitutions" which do not force the denotation - * of a TypeRef unless the name matches up with one of the substituted symbols. - */ - final class SafeSubstMap(from: List[Symbol], to: List[Type]) extends TypeMap { - def apply(tp: Type): Type = tp match { - case tp: NamedType => - try { - var sym: Symbol = null - var fs = from - var ts = to - while (fs.nonEmpty) { - if (fs.head.name == tp.name) { - if (sym == null) sym = tp.symbol - if (fs.head eq sym) return ts.head - } - fs = fs.tail - ts = ts.tail - } - tp.newLikeThis(apply(tp.prefix)) - } - catch { - case ex: CyclicReference => tp.derivedSelect(apply(tp.prefix)) - } - case _ => mapOver(tp) - } - } -} diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala deleted file mode 100644 index 8b7c28e19..000000000 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ /dev/null @@ -1,2004 +0,0 @@ -package dotty.tools -package dotc -package core - -import Periods._, Contexts._, Symbols._, Denotations._, Names._, NameOps._, Annotations._ -import Types._, Flags._, Decorators._, DenotTransformers._, StdNames._, Scopes._, Comments._ -import NameOps._ -import Scopes.Scope -import collection.mutable -import collection.immutable.BitSet -import scala.reflect.io.AbstractFile -import Decorators.SymbolIteratorDecorator -import ast._ -import annotation.tailrec -import CheckRealizable._ -import util.SimpleMap -import util.Stats -import config.Config -import config.Printers.{completions, incremental, noPrinter} - -trait SymDenotations { this: Context => - import SymDenotations._ - - /** Factory method for SymDenotion creation. All creations - * should be done via this method. - */ - def SymDenotation( - symbol: Symbol, - owner: Symbol, - name: Name, - initFlags: FlagSet, - initInfo: Type, - initPrivateWithin: Symbol = NoSymbol)(implicit ctx: Context): SymDenotation = { - val result = - if (symbol.isClass) - if (initFlags is Package) new PackageClassDenotation(symbol, owner, name, initFlags, initInfo, initPrivateWithin, ctx.runId) - else new ClassDenotation(symbol, owner, name, initFlags, initInfo, initPrivateWithin, ctx.runId) - else new SymDenotation(symbol, owner, name, initFlags, initInfo, initPrivateWithin) - result.validFor = stablePeriod - result - } - - def stillValid(denot: SymDenotation): Boolean = - if (denot.is(ValidForever) || denot.isRefinementClass || denot.isImport) true - else { - val initial = denot.initial - val firstPhaseId = initial.validFor.firstPhaseId.max(ctx.typerPhase.id) - if ((initial ne denot) || ctx.phaseId != firstPhaseId) - ctx.withPhase(firstPhaseId).stillValidInOwner(initial) - else - stillValidInOwner(denot) - } - - private[SymDenotations] def stillValidInOwner(denot: SymDenotation): Boolean = try { - val owner = denot.owner.denot - stillValid(owner) && ( - !owner.isClass - || owner.isRefinementClass - || owner.is(Scala2x) - || (owner.unforcedDecls.lookupAll(denot.name) contains denot.symbol) - || denot.isSelfSym) - } catch { - case ex: StaleSymbol => false - } - - /** Explain why symbol is invalid; used for debugging only */ - def traceInvalid(denot: Denotation): Boolean = { - def show(d: Denotation) = s"$d#${d.symbol.id}" - def explain(msg: String) = { - println(s"${show(denot)} is invalid at ${this.period} because $msg") - false - } - denot match { - case denot: SymDenotation => - def explainSym(msg: String) = explain(s"$msg\n defined = ${denot.definedPeriodsString}") - if (denot.is(ValidForever) || denot.isRefinementClass) true - else { - implicit val ctx: Context = this - val initial = denot.initial - if ((initial ne denot) || ctx.phaseId != initial.validFor.firstPhaseId) { - ctx.withPhase(initial.validFor.firstPhaseId).traceInvalid(initial) - } else try { - val owner = denot.owner.denot - if (!traceInvalid(owner)) explainSym("owner is invalid") - else if (!owner.isClass || owner.isRefinementClass || denot.isSelfSym) true - else if (owner.unforcedDecls.lookupAll(denot.name) contains denot.symbol) true - else explainSym(s"decls of ${show(owner)} are ${owner.unforcedDecls.lookupAll(denot.name).toList}, do not contain ${denot.symbol}") - } catch { - case ex: StaleSymbol => explainSym(s"$ex was thrown") - } - } - case _ => - explain("denotation is not a SymDenotation") - } - } -} - -object SymDenotations { - - /** A sym-denotation represents the contents of a definition - * during a period. - */ - class SymDenotation private[SymDenotations] ( - symbol: Symbol, - ownerIfExists: Symbol, - final val name: Name, - initFlags: FlagSet, - initInfo: Type, - initPrivateWithin: Symbol = NoSymbol) extends SingleDenotation(symbol) { - - //assert(symbol.id != 4940, name) - - override def hasUniqueSym: Boolean = exists - - /** Debug only - override def validFor_=(p: Period) = { - super.validFor_=(p) - } - */ - if (Config.checkNoSkolemsInInfo) assertNoSkolems(initInfo) - - // ------ Getting and setting fields ----------------------------- - - private[this] var myFlags: FlagSet = adaptFlags(initFlags) - private[this] var myInfo: Type = initInfo - private[this] var myPrivateWithin: Symbol = initPrivateWithin - private[this] var myAnnotations: List[Annotation] = Nil - - /** The owner of the symbol; overridden in NoDenotation */ - def owner: Symbol = ownerIfExists - - /** Same as owner, except returns NoSymbol for NoSymbol */ - def maybeOwner: Symbol = if (exists) owner else NoSymbol - - /** The flag set */ - final def flags(implicit ctx: Context): FlagSet = { ensureCompleted(); myFlags } - - /** The flag set without forcing symbol completion. - * Should be used only for printing. - */ - private[dotc] final def flagsUNSAFE = myFlags - - /** Adapt flag set to this denotation's term or type nature */ - private def adaptFlags(flags: FlagSet) = if (isType) flags.toTypeFlags else flags.toTermFlags - - /** Update the flag set */ - final def flags_=(flags: FlagSet): Unit = - myFlags = adaptFlags(flags) - - /** Set given flags(s) of this denotation */ - final def setFlag(flags: FlagSet): Unit = { myFlags |= flags } - - /** Unset given flags(s) of this denotation */ - final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags } - - /** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */ - final def setApplicableFlags(flags: FlagSet): Unit = { - val mask = if (myFlags.is(Trait)) NoInitsInterface else NoInits - setFlag(flags & mask) - } - - /** Has this denotation one of the flags in `fs` set? */ - final def is(fs: FlagSet)(implicit ctx: Context) = { - (if (fs <= FromStartFlags) myFlags else flags) is fs - } - - /** Has this denotation one of the flags in `fs` set, whereas none of the flags - * in `butNot` are set? - */ - final def is(fs: FlagSet, butNot: FlagSet)(implicit ctx: Context) = - (if (fs <= FromStartFlags && butNot <= FromStartFlags) myFlags else flags) is (fs, butNot) - - /** Has this denotation all of the flags in `fs` set? */ - final def is(fs: FlagConjunction)(implicit ctx: Context) = - (if (fs <= FromStartFlags) myFlags else flags) is fs - - /** Has this denotation all of the flags in `fs` set, whereas none of the flags - * in `butNot` are set? - */ - final def is(fs: FlagConjunction, butNot: FlagSet)(implicit ctx: Context) = - (if (fs <= FromStartFlags && butNot <= FromStartFlags) myFlags else flags) is (fs, butNot) - - /** The type info. - * The info is an instance of TypeType iff this is a type denotation - * Uncompleted denotations set myInfo to a LazyType. - */ - final def info(implicit ctx: Context): Type = myInfo match { - case myInfo: LazyType => completeFrom(myInfo); info - case _ => myInfo - } - - /** The type info, or, if symbol is not yet completed, the completer */ - final def infoOrCompleter = myInfo - - /** Optionally, the info if it is completed */ - final def unforcedInfo: Option[Type] = myInfo match { - case myInfo: LazyType => None - case _ => Some(myInfo) - } - - private def completeFrom(completer: LazyType)(implicit ctx: Context): Unit = { - if (completions ne noPrinter) { - completions.println(i"${" " * indent}completing ${if (isType) "type" else "val"} $name") - indent += 1 - } - if (myFlags is Touched) throw CyclicReference(this) - myFlags |= Touched - - // completions.println(s"completing ${this.debugString}") - try completer.complete(this)(ctx.withPhase(validFor.firstPhaseId)) - catch { - case ex: CyclicReference => - completions.println(s"error while completing ${this.debugString}") - throw ex - } - finally - if (completions ne noPrinter) { - indent -= 1 - completions.println(i"${" " * indent}completed $name in $owner") - } - // completions.println(s"completed ${this.debugString}") - } - - protected[dotc] def info_=(tp: Type) = { - /* // DEBUG - def illegal: String = s"illegal type for $this: $tp" - if (this is Module) // make sure module invariants that allow moduleClass and sourceModule to work are kept. - tp match { - case tp: ClassInfo => assert(tp.selfInfo.isInstanceOf[TermRefBySym], illegal) - case tp: NamedType => assert(tp.isInstanceOf[TypeRefBySym], illegal) - case tp: ExprType => assert(tp.resultType.isInstanceOf[TypeRefBySym], illegal) - case _ => - } - */ - if (Config.checkNoSkolemsInInfo) assertNoSkolems(tp) - myInfo = tp - } - - /** The name, except - * - if this is a module class, strip the module class suffix - * - if this is a companion object with a clash-avoiding name, strip the - * "avoid clash" suffix - */ - def effectiveName(implicit ctx: Context) = - if (this is ModuleClass) name.stripModuleClassSuffix - else name.stripAvoidClashSuffix - - /** The privateWithin boundary, NoSymbol if no boundary is given. - */ - final def privateWithin(implicit ctx: Context): Symbol = { ensureCompleted(); myPrivateWithin } - - /** Set privateWithin. */ - protected[core] final def privateWithin_=(sym: Symbol): Unit = - myPrivateWithin = sym - - /** The annotations of this denotation */ - final def annotations(implicit ctx: Context): List[Annotation] = { - ensureCompleted(); myAnnotations - } - - /** Update the annotations of this denotation */ - private[core] final def annotations_=(annots: List[Annotation]): Unit = - myAnnotations = annots - - /** Does this denotation have an annotation matching the given class symbol? */ - final def hasAnnotation(cls: Symbol)(implicit ctx: Context) = - dropOtherAnnotations(annotations, cls).nonEmpty - - /** Apply transform `f` to all annotations of this denotation */ - final def transformAnnotations(f: Annotation => Annotation)(implicit ctx: Context): Unit = - annotations = annotations.mapConserve(f) - - /** Keep only those annotations that satisfy `p` */ - final def filterAnnotations(p: Annotation => Boolean)(implicit ctx: Context): Unit = - annotations = annotations.filterConserve(p) - - /** Optionally, the annotation matching the given class symbol */ - final def getAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] = - dropOtherAnnotations(annotations, cls) match { - case annot :: _ => Some(annot) - case nil => None - } - - /** The same as getAnnotation, but without ensuring - * that the symbol carrying the annotation is completed - */ - final def unforcedAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] = - dropOtherAnnotations(myAnnotations, cls) match { - case annot :: _ => Some(annot) - case nil => None - } - - /** Add given annotation to the annotations of this denotation */ - final def addAnnotation(annot: Annotation): Unit = - annotations = annot :: myAnnotations - - /** Remove annotation with given class from this denotation */ - final def removeAnnotation(cls: Symbol)(implicit ctx: Context): Unit = - annotations = myAnnotations.filterNot(_ matches cls) - - /** Remove any annotations with same class as `annot`, and add `annot` */ - final def updateAnnotation(annot: Annotation)(implicit ctx: Context): Unit = { - removeAnnotation(annot.symbol) - addAnnotation(annot) - } - - /** Add all given annotations to this symbol */ - final def addAnnotations(annots: TraversableOnce[Annotation])(implicit ctx: Context): Unit = - annots.foreach(addAnnotation) - - @tailrec - private def dropOtherAnnotations(anns: List[Annotation], cls: Symbol)(implicit ctx: Context): List[Annotation] = anns match { - case ann :: rest => if (ann matches cls) anns else dropOtherAnnotations(rest, cls) - case Nil => Nil - } - - /** The denotation is completed: info is not a lazy type and attributes have defined values */ - final def isCompleted: Boolean = !myInfo.isInstanceOf[LazyType] - - /** The denotation is in train of being completed */ - final def isCompleting: Boolean = (myFlags is Touched) && !isCompleted - - /** The completer of this denotation. @pre: Denotation is not yet completed */ - final def completer: LazyType = myInfo.asInstanceOf[LazyType] - - /** Make sure this denotation is completed */ - final def ensureCompleted()(implicit ctx: Context): Unit = info - - /** The symbols defined in this class or object. - * Careful! This does not force the type, so is compilation order dependent. - * This method should be used only in the following circumstances: - * - * 1. When accessing type parameters or type parameter accessors (both are entered before - * completion). - * 2. When obtaining the current scope in order to enter, rename or delete something there. - * 3. When playing it safe in order not to raise CylicReferences, e.g. for printing things - * or taking more efficient shortcuts (e.g. the stillValid test). - */ - final def unforcedDecls(implicit ctx: Context): Scope = myInfo match { - case cinfo: LazyType => - val knownDecls = cinfo.decls - if (knownDecls ne EmptyScope) knownDecls - else { completeFrom(cinfo); unforcedDecls } // complete-once - case _ => info.decls - } - - /** If this is a package class, the symbols entered in it - * before it is completed. (this is needed to eagerly enter synthetic - * aliases such as AnyRef into a package class without forcing it. - * Right now, the only usage is for the AnyRef alias in Definitions. - */ - final private[core] def currentPackageDecls(implicit ctx: Context): MutableScope = myInfo match { - case pinfo: SymbolLoaders # PackageLoader => pinfo.currentDecls - case _ => unforcedDecls.openForMutations - } - - // ------ Names ---------------------------------------------- - - /** The expanded name of this denotation. */ - final def expandedName(implicit ctx: Context) = - if (is(ExpandedName) || isConstructor) name - else { - def legalize(name: Name): Name = // JVM method names may not contain `<' or `>' characters - if (is(Method)) name.replace('<', '(').replace('>', ')') else name - legalize(name.expandedName(initial.owner)) - } - // need to use initial owner to disambiguate, as multiple private symbols with the same name - // might have been moved from different origins into the same class - - /** The name with which the denoting symbol was created */ - final def originalName(implicit ctx: Context) = { - val d = initial - if (d is ExpandedName) d.name.unexpandedName else d.name // !!!DEBUG, was: effectiveName - } - - /** The encoded full path name of this denotation, where outer names and inner names - * are separated by `separator` strings. - * Never translates expansions of operators back to operator symbol. - * Drops package objects. Represents terms in the owner chain by a simple `~`. - * (Note: scalac uses nothing to represent terms, which can cause name clashes - * between same-named definitions in different enclosing methods. Before this commit - * we used `$' but this can cause ambiguities with the class separator '$'). - * A separator "" means "flat name"; the real separator in this case is "$" and - * enclosing packages do not form part of the name. - */ - def fullNameSeparated(separator: String)(implicit ctx: Context): Name = { - var sep = separator - var stopAtPackage = false - if (sep.isEmpty) { - sep = "$" - stopAtPackage = true - } - if (symbol == NoSymbol || - owner == NoSymbol || - owner.isEffectiveRoot || - stopAtPackage && owner.is(PackageClass)) name - else { - var encl = owner - while (!encl.isClass && !encl.isPackageObject) { - encl = encl.owner - sep += "~" - } - if (owner.is(ModuleClass, butNot = Package) && sep == "$") sep = "" // duplicate scalac's behavior: don't write a double '$$' for module class members. - val fn = encl.fullNameSeparated(separator) ++ sep ++ name - if (isType) fn.toTypeName else fn.toTermName - } - } - - /** The encoded flat name of this denotation, where joined names are separated by `separator` characters. */ - def flatName(implicit ctx: Context): Name = fullNameSeparated("") - - /** `fullName` where `.' is the separator character */ - def fullName(implicit ctx: Context): Name = fullNameSeparated(".") - - // ----- Tests ------------------------------------------------- - - /** Is this denotation a type? */ - override def isType: Boolean = name.isTypeName - - /** Is this denotation a class? */ - final def isClass: Boolean = isInstanceOf[ClassDenotation] - - /** Is this denotation a non-trait class? */ - final def isRealClass(implicit ctx: Context) = isClass && !is(Trait) - - /** Cast to class denotation */ - final def asClass: ClassDenotation = asInstanceOf[ClassDenotation] - - /** is this symbol the result of an erroneous definition? */ - def isError: Boolean = false - - /** Make denotation not exist */ - final def markAbsent(): Unit = - myInfo = NoType - - /** Is symbol known to not exist? */ - final def isAbsent(implicit ctx: Context): Boolean = - myInfo == NoType || - (this is (ModuleVal, butNot = Package)) && moduleClass.isAbsent - - /** Is this symbol the root class or its companion object? */ - final def isRoot: Boolean = - (name.toTermName == nme.ROOT || name == nme.ROOTPKG) && (owner eq NoSymbol) - - /** Is this symbol the empty package class or its companion object? */ - final def isEmptyPackage(implicit ctx: Context): Boolean = - name.toTermName == nme.EMPTY_PACKAGE && owner.isRoot - - /** Is this symbol the empty package class or its companion object? */ - final def isEffectiveRoot(implicit ctx: Context) = isRoot || isEmptyPackage - - /** Is this symbol an anonymous class? */ - final def isAnonymousClass(implicit ctx: Context): Boolean = - isClass && (initial.name startsWith tpnme.ANON_CLASS) - - final def isAnonymousFunction(implicit ctx: Context) = - this.symbol.is(Method) && (initial.name startsWith nme.ANON_FUN) - - final def isAnonymousModuleVal(implicit ctx: Context) = - this.symbol.is(ModuleVal) && (initial.name startsWith nme.ANON_CLASS) - - /** Is this a companion class method or companion object method? - * These methods are generated by Symbols#synthesizeCompanionMethod - * and used in SymDenotations#companionClass and - * SymDenotations#companionModule . - */ - final def isCompanionMethod(implicit ctx: Context) = - name.toTermName == nme.COMPANION_CLASS_METHOD || - name.toTermName == nme.COMPANION_MODULE_METHOD - - /** Is this a syntetic method that represents conversions between representations of a value class - * These methods are generated in ExtensionMethods - * and used in ElimErasedValueType. - */ - final def isValueClassConvertMethod(implicit ctx: Context) = - name.toTermName == nme.U2EVT || - name.toTermName == nme.EVT2U - - /** Is symbol a primitive value class? */ - def isPrimitiveValueClass(implicit ctx: Context) = - maybeOwner == defn.ScalaPackageClass && defn.ScalaValueClasses().contains(symbol) - - /** Is symbol a primitive numeric value class? */ - def isNumericValueClass(implicit ctx: Context) = - maybeOwner == defn.ScalaPackageClass && defn.ScalaNumericValueClasses().contains(symbol) - - /** Is symbol a phantom class for which no runtime representation exists? */ - def isPhantomClass(implicit ctx: Context) = defn.PhantomClasses contains symbol - - /** Is this symbol a class representing a refinement? These classes - * are used only temporarily in Typer and Unpickler as an intermediate - * step for creating Refinement types. - */ - final def isRefinementClass(implicit ctx: Context): Boolean = - name.decode == tpnme.REFINE_CLASS - - /** Is this symbol a package object or its module class? */ - def isPackageObject(implicit ctx: Context): Boolean = { - val poName = if (isType) nme.PACKAGE_CLS else nme.PACKAGE - (name.toTermName == poName) && (owner is Package) && (this is Module) - } - - /** Is this symbol an abstract type? */ - final def isAbstractType(implicit ctx: Context) = isType && (this is Deferred) - - /** Is this symbol an alias type? */ - final def isAliasType(implicit ctx: Context) = isAbstractOrAliasType && !(this is Deferred) - - /** Is this symbol an abstract or alias type? */ - final def isAbstractOrAliasType = isType & !isClass - - /** Is this the denotation of a self symbol of some class? - * This is the case if one of two conditions holds: - * 1. It is the symbol referred to in the selfInfo part of the ClassInfo - * which is the type of this symbol's owner. - * 2. This symbol is owned by a class, it's selfInfo field refers to a type - * (indicating the self definition does not introduce a name), and the - * symbol's name is "_". - * TODO: Find a more robust way to characterize self symbols, maybe by - * spending a Flag on them? - */ - final def isSelfSym(implicit ctx: Context) = owner.infoOrCompleter match { - case ClassInfo(_, _, _, _, selfInfo) => - selfInfo == symbol || - selfInfo.isInstanceOf[Type] && name == nme.WILDCARD - case _ => false - } - - /** Is this definition contained in `boundary`? - * Same as `ownersIterator contains boundary` but more efficient. - */ - final def isContainedIn(boundary: Symbol)(implicit ctx: Context): Boolean = { - def recur(sym: Symbol): Boolean = - if (sym eq boundary) true - else if (sym eq NoSymbol) false - else if ((sym is PackageClass) && !(boundary is PackageClass)) false - else recur(sym.owner) - recur(symbol) - } - - final def isProperlyContainedIn(boundary: Symbol)(implicit ctx: Context): Boolean = - symbol != boundary && isContainedIn(boundary) - - /** Is this denotation static (i.e. with no outer instance)? */ - final def isStatic(implicit ctx: Context) = - (this is JavaStatic) || this.exists && owner.isStaticOwner || this.isRoot - - /** Is this a package class or module class that defines static symbols? */ - final def isStaticOwner(implicit ctx: Context): Boolean = - (this is PackageClass) || (this is ModuleClass) && isStatic - - /** Is this denotation defined in the same scope and compilation unit as that symbol? */ - final def isCoDefinedWith(that: Symbol)(implicit ctx: Context) = - (this.effectiveOwner == that.effectiveOwner) && - ( !(this.effectiveOwner is PackageClass) - || this.isAbsent || that.isAbsent - || { // check if they are defined in the same file(or a jar) - val thisFile = this.symbol.associatedFile - val thatFile = that.symbol.associatedFile - ( thisFile == null - || thatFile == null - || thisFile.path == thatFile.path // Cheap possibly wrong check, then expensive normalization - || thisFile.canonicalPath == thatFile.canonicalPath - ) - } - ) - - /** Is this a denotation of a stable term (or an arbitrary type)? */ - final def isStable(implicit ctx: Context) = - isType || is(Stable) || !(is(UnstableValue) || info.isInstanceOf[ExprType]) - - /** Is this a "real" method? A real method is a method which is: - * - not an accessor - * - not a label - * - not an anonymous function - * - not a companion method - */ - final def isRealMethod(implicit ctx: Context) = - this.is(Method, butNot = AccessorOrLabel) && - !isAnonymousFunction && - !isCompanionMethod - - /** Is this a getter? */ - final def isGetter(implicit ctx: Context) = - (this is Accessor) && !originalName.isSetterName && !originalName.isScala2LocalSuffix - - /** Is this a setter? */ - final def isSetter(implicit ctx: Context) = - (this is Accessor) && - originalName.isSetterName && - (!isCompleted || info.firstParamTypes.nonEmpty) // to avoid being fooled by var x_= : Unit = ... - - /** is this a symbol representing an import? */ - final def isImport = name == nme.IMPORT - - /** is this the constructor of a class? */ - final def isClassConstructor = name == nme.CONSTRUCTOR - - /** Is this the constructor of a trait? */ - final def isImplClassConstructor = name == nme.TRAIT_CONSTRUCTOR - - /** Is this the constructor of a trait or a class */ - final def isConstructor = name.isConstructorName - - /** Is this a local template dummmy? */ - final def isLocalDummy: Boolean = name.isLocalDummyName - - /** Does this symbol denote the primary constructor of its enclosing class? */ - final def isPrimaryConstructor(implicit ctx: Context) = - isConstructor && owner.primaryConstructor == symbol - - /** Does this symbol denote the static constructor of its enclosing class? */ - final def isStaticConstructor(implicit ctx: Context) = - name.isStaticConstructorName - - /** Is this a subclass of the given class `base`? */ - def isSubClass(base: Symbol)(implicit ctx: Context) = false - - /** Is this a subclass of `base`, - * and is the denoting symbol also different from `Null` or `Nothing`? - * @note erroneous classes are assumed to derive from all other classes - * and all classes derive from them. - */ - def derivesFrom(base: Symbol)(implicit ctx: Context) = false - - /** Is this symbol a class that extends `AnyVal`? */ - final def isValueClass(implicit ctx: Context): Boolean = { - val di = initial - di.isClass && - di.derivesFrom(defn.AnyValClass)(ctx.withPhase(di.validFor.firstPhaseId)) - // We call derivesFrom at the initial phase both because AnyVal does not exist - // after Erasure and to avoid cyclic references caused by forcing denotations - } - - /** Is this symbol a class references to which that are supertypes of null? */ - final def isNullableClass(implicit ctx: Context): Boolean = - isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass - - /** Is this definition accessible as a member of tree with type `pre`? - * @param pre The type of the tree from which the selection is made - * @param superAccess Access is via super - * Everything is accessible if `pre` is `NoPrefix`. - * A symbol with type `NoType` is not accessible for any other prefix. - */ - final def isAccessibleFrom(pre: Type, superAccess: Boolean = false, whyNot: StringBuffer = null)(implicit ctx: Context): Boolean = { - - /** Are we inside definition of `boundary`? */ - def accessWithin(boundary: Symbol) = - ctx.owner.isContainedIn(boundary) && - (!(this is JavaDefined) || // disregard package nesting for Java - ctx.owner.enclosingPackageClass == boundary.enclosingPackageClass) - - /** Are we within definition of linked class of `boundary`? */ - def accessWithinLinked(boundary: Symbol) = { - val linked = boundary.linkedClass - (linked ne NoSymbol) && accessWithin(linked) - } - - /** Is `pre` the same as C.thisThis, where C is exactly the owner of this symbol, - * or, if this symbol is protected, a subclass of the owner? - */ - def isCorrectThisType(pre: Type): Boolean = pre match { - case pre: ThisType => - (pre.cls eq owner) || (this is Protected) && pre.cls.derivesFrom(owner) - case pre: TermRef => - pre.symbol.moduleClass == owner - case _ => - false - } - - /** Is protected access to target symbol permitted? */ - def isProtectedAccessOK = { - def fail(str: => String): Boolean = { - if (whyNot != null) whyNot append str - false - } - val cls = owner.enclosingSubClass - if (!cls.exists) - fail( - i""" - | Access to protected $this not permitted because enclosing ${ctx.owner.enclosingClass.showLocated} - | is not a subclass of ${owner.showLocated} where target is defined""") - else if ( - !( isType // allow accesses to types from arbitrary subclasses fixes #4737 - || pre.baseTypeRef(cls).exists // ??? why not use derivesFrom ??? - || isConstructor - || (owner is ModuleClass) // don't perform this check for static members - )) - fail( - i""" - | Access to protected ${symbol.show} not permitted because prefix type ${pre.widen.show} - | does not conform to ${cls.showLocated} where the access takes place""") - else true - } - - if (pre eq NoPrefix) true - else if (info eq NoType) false - else { - val boundary = accessBoundary(owner) - - ( boundary.isTerm - || boundary.isRoot - || (accessWithin(boundary) || accessWithinLinked(boundary)) && - ( !(this is Local) - || (owner is ImplClass) // allow private local accesses to impl class members - || isCorrectThisType(pre) - ) - || (this is Protected) && - ( superAccess - || pre.isInstanceOf[ThisType] - || ctx.phase.erasedTypes - || isProtectedAccessOK - ) - ) - } - } - - /** Do members of this symbol need translation via asSeenFrom when - * accessed via prefix `pre`? - */ - def membersNeedAsSeenFrom(pre: Type)(implicit ctx: Context) = - !( this.isTerm - || this.isStaticOwner - || ctx.erasedTypes - || (pre eq NoPrefix) || (pre eq thisType) - ) - - /** Is this symbol concrete, or that symbol deferred? */ - def isAsConcrete(that: Symbol)(implicit ctx: Context): Boolean = - !(this is Deferred) || (that is Deferred) - - /** Does this symbol have defined or inherited default parameters? */ - def hasDefaultParams(implicit ctx: Context): Boolean = - if (this is HasDefaultParams) true - else if (this is NoDefaultParams) false - else { - val result = allOverriddenSymbols exists (_.hasDefaultParams) - setFlag(if (result) InheritedDefaultParams else NoDefaultParams) - result - } - - /** Symbol is an owner that would be skipped by effectiveOwner. Skipped are - * - package objects - * - labels - * - non-lazy valdefs - */ - def isWeakOwner(implicit ctx: Context): Boolean = - isPackageObject || - isTerm && !is(MethodOrLazy, butNot = Label) && !isLocalDummy - - // def isOverridable: Boolean = !!! need to enforce that classes cannot be redefined - def isSkolem: Boolean = name == nme.SKOLEM - - def isInlineMethod(implicit ctx: Context): Boolean = is(InlineMethod, butNot = Accessor) - - // ------ access to related symbols --------------------------------- - - /* Modules and module classes are represented as follows: - * - * object X extends Y { def f() } - * - * <module> lazy val X: X$ = new X$ - * <module> class X$ extends Y { this: X.type => def f() } - * - * During completion, references to moduleClass and sourceModules are stored in - * the completers. - */ - /** The class implementing this module, NoSymbol if not applicable. */ - final def moduleClass(implicit ctx: Context): Symbol = { - def notFound = { println(s"missing module class for $name: $myInfo"); NoSymbol } - if (this is ModuleVal) - myInfo match { - case info: TypeRef => info.symbol - case ExprType(info: TypeRef) => info.symbol // needed after uncurry, when module terms might be accessor defs - case info: LazyType => info.moduleClass - case t: MethodType => - t.resultType match { - case info: TypeRef => info.symbol - case _ => notFound - } - case _ => notFound - } - else NoSymbol - } - - /** The module implemented by this module class, NoSymbol if not applicable. */ - final def sourceModule(implicit ctx: Context): Symbol = myInfo match { - case ClassInfo(_, _, _, _, selfType) if this is ModuleClass => - selfType match { - case selfType: TermRef => selfType.symbol - case selfType: Symbol => selfType.info.asInstanceOf[TermRef].symbol - } - case info: LazyType => - info.sourceModule - case _ => - NoSymbol - } - - /** The field accessed by this getter or setter, or if it does not exist, the getter */ - def accessedFieldOrGetter(implicit ctx: Context): Symbol = { - val fieldName = if (isSetter) name.asTermName.getterName else name - val d = owner.info.decl(fieldName) - val field = d.suchThat(!_.is(Method)).symbol - def getter = d.suchThat(_.info.isParameterless).symbol - field orElse getter - } - - /** The field accessed by a getter or setter, or - * if it does not exists, the getter of a setter, or - * if that does not exist the symbol itself. - */ - def underlyingSymbol(implicit ctx: Context): Symbol = - if (is(Accessor)) accessedFieldOrGetter orElse symbol else symbol - - /** The chain of owners of this denotation, starting with the denoting symbol itself */ - final def ownersIterator(implicit ctx: Context) = new Iterator[Symbol] { - private[this] var current = symbol - def hasNext = current.exists - def next: Symbol = { - val result = current - current = current.owner - result - } - } - - /** If this is a weak owner, its owner, otherwise the denoting symbol. */ - final def skipWeakOwner(implicit ctx: Context): Symbol = - if (isWeakOwner) owner.skipWeakOwner else symbol - - /** The owner, skipping package objects, labels and non-lazy valdefs. */ - final def effectiveOwner(implicit ctx: Context) = owner.skipWeakOwner - - /** The class containing this denotation. - * If this denotation is already a class, return itself - * Definitions flagged with InSuperCall are treated specially. - * Their enclosing class is not the lexically enclosing class, - * but in turn the enclosing class of the latter. This reflects - * the context created by `Context#superCallContext`, `Context#thisCallArgContext` - * for these definitions. - * - * Note, that as packages have ClassSymbols, top level classes will have an `enclosingClass` - * with Package flag set. - */ - final def enclosingClass(implicit ctx: Context): Symbol = { - def enclClass(sym: Symbol, skip: Boolean): Symbol = { - def newSkip = sym.is(InSuperCall) || sym.is(JavaStaticTerm) - if (!sym.exists) - NoSymbol - else if (sym.isClass) - if (skip) enclClass(sym.owner, newSkip) else sym - else - enclClass(sym.owner, skip || newSkip) - } - enclClass(symbol, false) - } - - /** A class that in source code would be lexically enclosing */ - final def lexicallyEnclosingClass(implicit ctx: Context): Symbol = - if (!exists || isClass) symbol else owner.lexicallyEnclosingClass - - /** A symbol is effectively final if it cannot be overridden in a subclass */ - final def isEffectivelyFinal(implicit ctx: Context): Boolean = - is(PrivateOrFinalOrInline) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass - - /** The class containing this denotation which has the given effective name. */ - final def enclosingClassNamed(name: Name)(implicit ctx: Context): Symbol = { - val cls = enclosingClass - if (cls.effectiveName == name || !cls.exists) cls else cls.owner.enclosingClassNamed(name) - } - - /** The closest enclosing method containing this definition. - * A local dummy owner is mapped to the primary constructor of the class. - */ - final def enclosingMethod(implicit ctx: Context): Symbol = - if (this is (Method, butNot = Label)) symbol - else if (this.isClass) primaryConstructor - else if (this.exists) owner.enclosingMethod - else NoSymbol - - /** The top-level class containing this denotation, - * except for a toplevel module, where its module class is returned. - */ - final def topLevelClass(implicit ctx: Context): Symbol = { - def topLevel(d: SymDenotation): Symbol = { - if (d.isEffectiveRoot || (d is PackageClass) || (d.owner is PackageClass)) d.symbol - else topLevel(d.owner) - } - val sym = topLevel(this) - if (sym.isClass) sym else sym.moduleClass - } - - /** The package class containing this denotation */ - final def enclosingPackageClass(implicit ctx: Context): Symbol = - if (this is PackageClass) symbol else owner.enclosingPackageClass - - /** The module object with the same (term-) name as this class or module class, - * and which is also defined in the same scope and compilation unit. - * NoSymbol if this module does not exist. - */ - final def companionModule(implicit ctx: Context): Symbol = { - if (this.flagsUNSAFE is Flags.Module) this.sourceModule - else { - val companionMethod = info.decls.denotsNamed(nme.COMPANION_MODULE_METHOD, selectPrivate).first - if (companionMethod.exists) - companionMethod.info.resultType.classSymbol.sourceModule - else - NoSymbol - } - } - - - /** The class with the same (type-) name as this module or module class, - * and which is also defined in the same scope and compilation unit. - * NoSymbol if this class does not exist. - */ - final def companionClass(implicit ctx: Context): Symbol = { - val companionMethod = info.decls.denotsNamed(nme.COMPANION_CLASS_METHOD, selectPrivate).first - - if (companionMethod.exists) - companionMethod.info.resultType.classSymbol - else - NoSymbol - } - - final def scalacLinkedClass(implicit ctx: Context): Symbol = - if (this is ModuleClass) companionNamed(effectiveName.toTypeName) - else if (this.isClass) companionNamed(effectiveName.moduleClassName).sourceModule.moduleClass - else NoSymbol - - - /** Find companion class symbol with given name, or NoSymbol if none exists. - * Three alternative strategies: - * 1. If owner is a class, look in its members, otherwise - * 2. If current compilation unit has a typed tree, - * determine the defining statement sequence and search its trees, otherwise - * 3. If context has an enclosing scope which defines this symbol, - * lookup its companion in the same scope. - */ - private def companionNamed(name: TypeName)(implicit ctx: Context): Symbol = - if (owner.isClass) - owner.info.decl(name).suchThat(_.isCoDefinedWith(symbol)).symbol - else if (!owner.exists || ctx.compilationUnit == null) - NoSymbol - else if (!ctx.compilationUnit.tpdTree.isEmpty) - tpd.definingStats(symbol).iterator - .map(tpd.definedSym) - .find(_.name == name) - .getOrElse(NoSymbol) - else if (ctx.scope == null) - NoSymbol - else if (ctx.scope.lookup(this.name) == symbol) - ctx.scope.lookup(name) - else - companionNamed(name)(ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next) - - /** If this is a class, the module class of its companion object. - * If this is a module class, its companion class. - * NoSymbol otherwise. - */ - final def linkedClass(implicit ctx: Context): Symbol = - if (this is ModuleClass) companionClass - else if (this.isClass) companionModule.moduleClass - else NoSymbol - - /** The class that encloses the owner of the current context - * and that is a subclass of this class. NoSymbol if no such class exists. - */ - final def enclosingSubClass(implicit ctx: Context) = - ctx.owner.ownersIterator.findSymbol(_.isSubClass(symbol)) - - /** The non-private symbol whose name and type matches the type of this symbol - * in the given class. - * @param inClass The class containing the result symbol's definition - * @param site The base type from which member types are computed - * - * inClass <-- find denot.symbol class C { <-- symbol is here - * - * site: Subtype of both inClass and C - */ - final def matchingDecl(inClass: Symbol, site: Type)(implicit ctx: Context): Symbol = { - var denot = inClass.info.nonPrivateDecl(name) - if (denot.isTerm) // types of the same name always match - denot = denot.matchingDenotation(site, site.memberInfo(symbol)) - denot.symbol - } - - /** The non-private member of `site` whose name and type matches the type of this symbol - */ - final def matchingMember(site: Type)(implicit ctx: Context): Symbol = { - var denot = site.nonPrivateMember(name) - if (denot.isTerm) // types of the same name always match - denot = denot.matchingDenotation(site, site.memberInfo(symbol)) - denot.symbol - } - - /** If false, this symbol cannot possibly participate in an override, - * either as overrider or overridee. - */ - final def canMatchInheritedSymbols(implicit ctx: Context): Boolean = - maybeOwner.isClass && memberCanMatchInheritedSymbols - - /** If false, this class member cannot possibly participate in an override, - * either as overrider or overridee. - */ - final def memberCanMatchInheritedSymbols(implicit ctx: Context): Boolean = - !isConstructor && !is(Private) - - /** The symbol, in class `inClass`, that is overridden by this denotation. */ - final def overriddenSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol = - if (!canMatchInheritedSymbols && (owner ne inClass)) NoSymbol - else matchingDecl(inClass, owner.thisType) - - /** All symbols overriden by this denotation. */ - final def allOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] = - if (!canMatchInheritedSymbols) Iterator.empty - else overriddenFromType(owner.info) - - /** Returns all matching symbols defined in parents of the selftype. */ - final def extendedOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] = - if (!canMatchInheritedSymbols) Iterator.empty - else overriddenFromType(owner.asClass.classInfo.selfType) - - private def overriddenFromType(tp: Type)(implicit ctx: Context): Iterator[Symbol] = - tp.baseClasses.tail.iterator map overriddenSymbol filter (_.exists) - - /** The symbol overriding this symbol in given subclass `ofclazz`. - * - * @param ofclazz is a subclass of this symbol's owner - */ - final def overridingSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol = - if (canMatchInheritedSymbols) matchingDecl(inClass, inClass.thisType) - else NoSymbol - - /** The symbol accessed by a super in the definition of this symbol when - * seen from class `base`. This symbol is always concrete. - * pre: `this.owner` is in the base class sequence of `base`. - */ - final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = { - def loop(bcs: List[ClassSymbol]): Symbol = bcs match { - case bc :: bcs1 => - val sym = matchingDecl(bcs.head, base.thisType) - .suchThat(alt => !(alt is Deferred)).symbol - if (sym.exists) sym else loop(bcs.tail) - case _ => - NoSymbol - } - loop(base.info.baseClasses.dropWhile(owner != _).tail) - } - - /** A member of class `base` is incomplete if - * (1) it is declared deferred or - * (2) it is abstract override and its super symbol in `base` is - * nonexistent or incomplete. - */ - final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean = - (this is Deferred) || - (this is AbsOverride) && { - val supersym = superSymbolIn(base) - supersym == NoSymbol || supersym.isIncompleteIn(base) - } - - /** The class or term symbol up to which this symbol is accessible, - * or RootClass if it is public. As java protected statics are - * otherwise completely inaccessible in scala, they are treated - * as public. - * @param base The access boundary to assume if this symbol is protected - */ - final def accessBoundary(base: Symbol)(implicit ctx: Context): Symbol = { - val fs = flags - if (fs is Private) owner - else if (fs is StaticProtected) defn.RootClass - else if (privateWithin.exists && !ctx.phase.erasedTypes) privateWithin - else if (fs is Protected) base - else defn.RootClass - } - - /** The primary constructor of a class or trait, NoSymbol if not applicable. */ - def primaryConstructor(implicit ctx: Context): Symbol = NoSymbol - - // ----- type-related ------------------------------------------------ - - /** The type parameters of a class symbol, Nil for all other symbols */ - def typeParams(implicit ctx: Context): List[TypeSymbol] = Nil - - /** The named type parameters declared or inherited by this symbol */ - def namedTypeParams(implicit ctx: Context): Set[TypeSymbol] = Set() - - /** The type This(cls), where cls is this class, NoPrefix for all other symbols */ - def thisType(implicit ctx: Context): Type = NoPrefix - - override def typeRef(implicit ctx: Context): TypeRef = - TypeRef(owner.thisType, name.asTypeName, this) - - override def termRef(implicit ctx: Context): TermRef = - TermRef(owner.thisType, name.asTermName, this) - - override def valRef(implicit ctx: Context): TermRef = - TermRef.withSigAndDenot(owner.thisType, name.asTermName, Signature.NotAMethod, this) - - override def termRefWithSig(implicit ctx: Context): TermRef = - TermRef.withSigAndDenot(owner.thisType, name.asTermName, signature, this) - - def nonMemberTermRef(implicit ctx: Context): TermRef = - TermRef.withFixedSym(owner.thisType, name.asTermName, symbol.asTerm) - - /** The variance of this type parameter or type member as an Int, with - * +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter - */ - final def variance(implicit ctx: Context): Int = - if (this is Covariant) 1 - else if (this is Contravariant) -1 - else 0 - - /** The flags to be used for a type parameter owned by this symbol. - * Overridden by ClassDenotation. - */ - def typeParamCreationFlags: FlagSet = TypeParam - - override def toString = { - val kindString = - if (myFlags is ModuleClass) "module class" - else if (isClass) "class" - else if (isType) "type" - else if (myFlags is Module) "module" - else if (myFlags is Method) "method" - else "val" - s"$kindString $name" - } - - // ----- Sanity checks and debugging */ - - def debugString = toString + "#" + symbol.id // !!! DEBUG - - def hasSkolems(tp: Type): Boolean = tp match { - case tp: SkolemType => true - case tp: NamedType => hasSkolems(tp.prefix) - case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo) - case tp: RecType => hasSkolems(tp.parent) - case tp: PolyType => tp.paramBounds.exists(hasSkolems) || hasSkolems(tp.resType) - case tp: MethodType => tp.paramTypes.exists(hasSkolems) || hasSkolems(tp.resType) - case tp: ExprType => hasSkolems(tp.resType) - case tp: HKApply => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems) - case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) - case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi) - case tp: AnnotatedType => hasSkolems(tp.tpe) - case tp: TypeVar => hasSkolems(tp.inst) - case _ => false - } - - def assertNoSkolems(tp: Type) = - if (!this.isSkolem) - assert(!hasSkolems(tp), s"assigning type $tp containing skolems to $this") - - // ----- copies and transforms ---------------------------------------- - - protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = new UniqueRefDenotation(s, i, validFor) - - /** Copy this denotation, overriding selective fields */ - final def copySymDenotation( - symbol: Symbol = this.symbol, - owner: Symbol = this.owner, - name: Name = this.name, - initFlags: FlagSet = UndefinedFlags, - info: Type = null, - privateWithin: Symbol = null, - annotations: List[Annotation] = null)(implicit ctx: Context) = - { // simulate default parameters, while also passing implicit context ctx to the default values - val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags) &~ Frozen - val info1 = if (info != null) info else this.info - val privateWithin1 = if (privateWithin != null) privateWithin else this.privateWithin - val annotations1 = if (annotations != null) annotations else this.annotations - val d = ctx.SymDenotation(symbol, owner, name, initFlags1, info1, privateWithin1) - d.annotations = annotations1 - d - } - - override def initial: SymDenotation = super.initial.asSymDenotation - - /** Install this denotation as the result of the given denotation transformer. */ - override def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = - super.installAfter(phase) - - /** Apply a transformation `f` to all denotations in this group that start at or after - * given phase. Denotations are replaced while keeping the same validity periods. - */ - override def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = - super.transformAfter(phase, f) - - /** If denotation is private, remove the Private flag and expand the name if necessary */ - def ensureNotPrivate(implicit ctx: Context) = - if (is(Private)) - copySymDenotation( - name = expandedName, - initFlags = this.flags &~ Private | ExpandedName) - else this - } - - /** The contents of a class definition during a period - */ - class ClassDenotation private[SymDenotations] ( - symbol: Symbol, - ownerIfExists: Symbol, - name: Name, - initFlags: FlagSet, - initInfo: Type, - initPrivateWithin: Symbol, - initRunId: RunId) - extends SymDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin) { - - import util.LRUCache - - // ----- denotation fields and accessors ------------------------------ - - if (initFlags is (Module, butNot = Package)) assert(name.isModuleClassName, s"module naming inconsistency: $name") - - /** The symbol asserted to have type ClassSymbol */ - def classSymbol: ClassSymbol = symbol.asInstanceOf[ClassSymbol] - - /** The info asserted to have type ClassInfo */ - def classInfo(implicit ctx: Context): ClassInfo = info.asInstanceOf[ClassInfo] - - /** TODO: Document why caches are supposedly safe to use */ - private[this] var myTypeParams: List[TypeSymbol] = _ - - private[this] var myNamedTypeParams: Set[TypeSymbol] = _ - - /** The type parameters in this class, in the order they appear in the current - * scope `decls`. This might be temporarily the incorrect order when - * reading Scala2 pickled info. The problem is fixed by `updateTypeParams` - * which is called once an unpickled symbol has been completed. - */ - private def typeParamsFromDecls(implicit ctx: Context) = - unforcedDecls.filter(sym => - (sym is TypeParam) && sym.owner == symbol).asInstanceOf[List[TypeSymbol]] - - /** The type parameters of this class */ - override final def typeParams(implicit ctx: Context): List[TypeSymbol] = { - if (myTypeParams == null) - myTypeParams = - if (ctx.erasedTypes || is(Module)) Nil // fast return for modules to avoid scanning package decls - else { - val di = initial - if (this ne di) di.typeParams - else infoOrCompleter match { - case info: TypeParamsCompleter => info.completerTypeParams(symbol) - case _ => typeParamsFromDecls - } - } - myTypeParams - } - - /** The named type parameters declared or inherited by this class */ - override final def namedTypeParams(implicit ctx: Context): Set[TypeSymbol] = { - def computeNamedTypeParams: Set[TypeSymbol] = - if (ctx.erasedTypes || is(Module)) Set() // fast return for modules to avoid scanning package decls - else memberNames(abstractTypeNameFilter).map(name => - info.member(name).symbol.asType).filter(_.is(TypeParam, butNot = ExpandedName)).toSet - if (myNamedTypeParams == null) myNamedTypeParams = computeNamedTypeParams - myNamedTypeParams - } - - override protected[dotc] final def info_=(tp: Type) = { - super.info_=(tp) - myTypeParams = null // changing the info might change decls, and with it typeParams - } - - /** The denotations of all parents in this class. */ - def classParents(implicit ctx: Context): List[TypeRef] = info match { - case classInfo: ClassInfo => classInfo.classParents - case _ => Nil - } - - /** The symbol of the superclass, NoSymbol if no superclass exists */ - def superClass(implicit ctx: Context): Symbol = classParents match { - case parent :: _ => - val cls = parent.classSymbol - if (cls is Trait) NoSymbol else cls - case _ => - NoSymbol - } - - /** The denotation is fully completed: all attributes are fully defined. - * ClassDenotations compiled from source are first completed, then fully completed. - * Packages are never fully completed since members can be added at any time. - * @see Namer#ClassCompleter - */ - private def isFullyCompleted(implicit ctx: Context): Boolean = { - def isFullyCompletedRef(tp: TypeRef) = tp.denot match { - case d: ClassDenotation => d.isFullyCompleted - case _ => false - } - def testFullyCompleted = - if (classParents.isEmpty) !is(Package) && symbol.eq(defn.AnyClass) - else classParents.forall(isFullyCompletedRef) - flagsUNSAFE.is(FullyCompleted) || - isCompleted && testFullyCompleted && { setFlag(FullyCompleted); true } - } - - // ------ syncing inheritance-related info ----------------------------- - - private var firstRunId: RunId = initRunId - - /** invalidate caches influenced by parent classes if one of the parents - * is younger than the denotation itself. - */ - override def syncWithParents(implicit ctx: Context): SingleDenotation = { - def isYounger(tref: TypeRef) = tref.symbol.denot match { - case denot: ClassDenotation => - if (denot.validFor.runId < ctx.runId) denot.current // syncs with its parents in turn - val result = denot.firstRunId > this.firstRunId - if (result) incremental.println(s"$denot is younger than $this") - result - case _ => false - } - val parentIsYounger = (firstRunId < ctx.runId) && { - infoOrCompleter match { - case cinfo: ClassInfo => cinfo.classParents exists isYounger - case _ => false - } - } - if (parentIsYounger) { - incremental.println(s"parents of $this are invalid; symbol id = ${symbol.id}, copying ...\n") - invalidateInheritedInfo() - } - firstRunId = ctx.runId - this - } - - /** Invalidate all caches and fields that depend on base classes and their contents */ - override def invalidateInheritedInfo(): Unit = { - myBaseClasses = null - mySuperClassBits = null - myMemberFingerPrint = FingerPrint.unknown - myMemberCache = null - myMemberCachePeriod = Nowhere - memberNamesCache = SimpleMap.Empty - } - - // ------ class-specific operations ----------------------------------- - - private[this] var myThisType: Type = null - - /** The this-type depends on the kind of class: - * - for a package class `p`: ThisType(TypeRef(Noprefix, p)) - * - for a module class `m`: A term ref to m's source module. - * - for all other classes `c` with owner `o`: ThisType(TypeRef(o.thisType, c)) - */ - override def thisType(implicit ctx: Context): Type = { - if (myThisType == null) myThisType = computeThisType - myThisType - } - - private def computeThisType(implicit ctx: Context): Type = - ThisType.raw( - TypeRef(if (this is Package) NoPrefix else owner.thisType, symbol.asType)) -/* else { - val pre = owner.thisType - if (this is Module) - if (isMissing(pre)) TermRef(pre, sourceModule.asTerm) - else TermRef.withSig(pre, name.sourceModuleName, Signature.NotAMethod) - else ThisType.raw(TypeRef(pre, symbol.asType)) - } -*/ - private[this] var myTypeRef: TypeRef = null - - override def typeRef(implicit ctx: Context): TypeRef = { - if (myTypeRef == null) myTypeRef = super.typeRef - myTypeRef - } - - private[this] var myBaseClasses: List[ClassSymbol] = null - private[this] var mySuperClassBits: BitSet = null - - /** Invalidate baseTypeRefCache, baseClasses and superClassBits on new run */ - private def checkBasesUpToDate()(implicit ctx: Context) = - if (baseTypeRefValid != ctx.runId) { - baseTypeRefCache = new java.util.HashMap[CachedType, Type] - myBaseClasses = null - mySuperClassBits = null - baseTypeRefValid = ctx.runId - } - - private def computeBases(implicit ctx: Context): (List[ClassSymbol], BitSet) = { - if (myBaseClasses eq Nil) throw CyclicReference(this) - myBaseClasses = Nil - val seen = new mutable.BitSet - val locked = new mutable.BitSet - def addBaseClasses(bcs: List[ClassSymbol], to: List[ClassSymbol]) - : List[ClassSymbol] = bcs match { - case bc :: bcs1 => - val bcs1added = addBaseClasses(bcs1, to) - val id = bc.superId - if (seen contains id) bcs1added - else { - seen += id - bc :: bcs1added - } - case nil => - to - } - def addParentBaseClasses(ps: List[Type], to: List[ClassSymbol]): List[ClassSymbol] = ps match { - case p :: ps1 => - addParentBaseClasses(ps1, addBaseClasses(p.baseClasses, to)) - case nil => - to - } - val bcs = classSymbol :: addParentBaseClasses(classParents, Nil) - val scbits = seen.toImmutable - if (isFullyCompleted) { - myBaseClasses = bcs - mySuperClassBits = scbits - } - else myBaseClasses = null - (bcs, scbits) - } - - /** A bitset that contains the superId's of all base classes */ - private def superClassBits(implicit ctx: Context): BitSet = - if (classParents.isEmpty) BitSet() // can happen when called too early in Namers - else { - checkBasesUpToDate() - if (mySuperClassBits != null) mySuperClassBits else computeBases._2 - } - - /** The base classes of this class in linearization order, - * with the class itself as first element. - */ - def baseClasses(implicit ctx: Context): List[ClassSymbol] = - if (classParents.isEmpty) classSymbol :: Nil // can happen when called too early in Namers - else { - checkBasesUpToDate() - if (myBaseClasses != null) myBaseClasses else computeBases._1 - } - - final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean = - !isAbsent && - base.isClass && - ( (symbol eq base) - || (superClassBits contains base.superId) - || (this is Erroneous) - || (base is Erroneous) - ) - - final override def isSubClass(base: Symbol)(implicit ctx: Context) = - derivesFrom(base) || - base.isClass && ( - (symbol eq defn.NothingClass) || - (symbol eq defn.NullClass) && (base ne defn.NothingClass)) - - final override def typeParamCreationFlags = ClassTypeParamCreationFlags - - private[this] var myMemberFingerPrint: FingerPrint = FingerPrint.unknown - - private def computeMemberFingerPrint(implicit ctx: Context): FingerPrint = { - var fp = FingerPrint() - var e = info.decls.lastEntry - while (e != null) { - fp.include(e.name) - e = e.prev - } - var ps = classParents - while (ps.nonEmpty) { - val parent = ps.head.typeSymbol - parent.denot match { - case parentDenot: ClassDenotation => - fp.include(parentDenot.memberFingerPrint) - if (parentDenot.isFullyCompleted) parentDenot.setFlag(Frozen) - case _ => - } - ps = ps.tail - } - fp - } - - /** A bloom filter for the names of all members in this class. - * Makes sense only for parent classes, and should definitely - * not be used for package classes because cache never - * gets invalidated. - */ - def memberFingerPrint(implicit ctx: Context): FingerPrint = - if (myMemberFingerPrint != FingerPrint.unknown) myMemberFingerPrint - else { - val fp = computeMemberFingerPrint - if (isFullyCompleted) myMemberFingerPrint = fp - fp - } - - private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null - private[this] var myMemberCachePeriod: Period = Nowhere - - private def memberCache(implicit ctx: Context): LRUCache[Name, PreDenotation] = { - if (myMemberCachePeriod != ctx.period) { - myMemberCache = new LRUCache - myMemberCachePeriod = ctx.period - } - myMemberCache - } - - /** Enter a symbol in current scope, and future scopes of same denotation. - * Note: We require that this does not happen after the first time - * someone does a findMember on a subclass. - * @param scope The scope in which symbol should be entered. - * If this is EmptyScope, the scope is `decls`. - */ - def enter(sym: Symbol, scope: Scope = EmptyScope)(implicit ctx: Context): Unit = { - val mscope = scope match { - case scope: MutableScope => - // if enter gets a scope as an argument, - // than this is a scope that will eventually become decls of this symbol. - // And this should only happen if this is first time the scope of symbol - // is computed, ie symbol yet has no future. - assert(this.nextInRun.validFor.code <= this.validFor.code) - scope - case _ => unforcedDecls.openForMutations - } - if (this is PackageClass) { - val entry = mscope.lookupEntry(sym.name) - if (entry != null) { - if (entry.sym == sym) return - mscope.unlink(entry) - entry.sym.denot = sym.denot // to avoid stale symbols - } - } - enterNoReplace(sym, mscope) - val nxt = this.nextInRun - if (nxt.validFor.code > this.validFor.code) { - this.nextInRun.asSymDenotation.asClass.enter(sym) - } - } - - /** Enter a symbol in given `scope` without potentially replacing the old copy. */ - def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = { - def isUsecase = ctx.docCtx.isDefined && sym.name.show.takeRight(4) == "$doc" - require( - (sym.denot.flagsUNSAFE is Private) || - !(this is Frozen) || - (scope ne this.unforcedDecls) || - sym.hasAnnotation(defn.ScalaStaticAnnot) || - sym.name.isInlineAccessor || - isUsecase) - - scope.enter(sym) - - if (myMemberFingerPrint != FingerPrint.unknown) - myMemberFingerPrint.include(sym.name) - if (myMemberCache != null) - myMemberCache invalidate sym.name - } - - /** Replace symbol `prev` (if defined in current class) by symbol `replacement`. - * If `prev` is not defined in current class, do nothing. - * @pre `prev` and `replacement` have the same name. - */ - def replace(prev: Symbol, replacement: Symbol)(implicit ctx: Context): Unit = { - require(!(this is Frozen)) - unforcedDecls.openForMutations.replace(prev, replacement) - if (myMemberCache != null) - myMemberCache invalidate replacement.name - } - - /** Delete symbol from current scope. - * Note: We require that this does not happen after the first time - * someone does a findMember on a subclass. - */ - def delete(sym: Symbol)(implicit ctx: Context) = { - require(!(this is Frozen)) - info.decls.openForMutations.unlink(sym) - myMemberFingerPrint = FingerPrint.unknown - if (myMemberCache != null) myMemberCache invalidate sym.name - } - - /** Make sure the type parameters of this class appear in the order given - * by `typeParams` in the scope of the class. Reorder definitions in scope if necessary. - */ - def ensureTypeParamsInCorrectOrder()(implicit ctx: Context): Unit = { - val tparams = typeParams - if (!ctx.erasedTypes && !typeParamsFromDecls.corresponds(tparams)(_.name == _.name)) { - val decls = info.decls - val decls1 = newScope - for (tparam <- typeParams) decls1.enter(decls.lookup(tparam.name)) - for (sym <- decls) if (!tparams.contains(sym)) decls1.enter(sym) - info = classInfo.derivedClassInfo(decls = decls1) - myTypeParams = null - } - } - - /** All members of this class that have the given name. - * The elements of the returned pre-denotation all - * have existing symbols. - */ - final def membersNamed(name: Name)(implicit ctx: Context): PreDenotation = { - val privates = info.decls.denotsNamed(name, selectPrivate) - privates union nonPrivateMembersNamed(name).filterDisjoint(privates) - } - - /** All non-private members of this class that have the given name. - * The elements of the returned pre-denotation all - * have existing symbols. - * @param inherited The method is called on a parent class from computeNPMembersNamed - */ - final def nonPrivateMembersNamed(name: Name, inherited: Boolean = false)(implicit ctx: Context): PreDenotation = { - Stats.record("nonPrivateMembersNamed") - if (Config.cacheMembersNamed) { - var denots: PreDenotation = memberCache lookup name - if (denots == null) { - denots = computeNPMembersNamed(name, inherited) - if (isFullyCompleted) memberCache.enter(name, denots) - } else if (Config.checkCacheMembersNamed) { - val denots1 = computeNPMembersNamed(name, inherited) - assert(denots.exists == denots1.exists, s"cache inconsistency: cached: $denots, computed $denots1, name = $name, owner = $this") - } - denots - } else computeNPMembersNamed(name, inherited) - } - - private[core] def computeNPMembersNamed(name: Name, inherited: Boolean)(implicit ctx: Context): PreDenotation = /*>|>*/ Stats.track("computeNPMembersNamed") /*<|<*/ { - if (!inherited || - !Config.useFingerPrints || - (memberFingerPrint contains name)) { - Stats.record("computeNPMembersNamed after fingerprint") - ensureCompleted() - val ownDenots = info.decls.denotsNamed(name, selectNonPrivate) - if (debugTrace) // DEBUG - println(s"$this.member($name), ownDenots = $ownDenots") - def collect(denots: PreDenotation, parents: List[TypeRef]): PreDenotation = parents match { - case p :: ps => - val denots1 = collect(denots, ps) - p.symbol.denot match { - case parentd: ClassDenotation => - denots1 union - parentd.nonPrivateMembersNamed(name, inherited = true) - .mapInherited(ownDenots, denots1, thisType) - case _ => - denots1 - } - case nil => - denots - } - if (name.isConstructorName) ownDenots - else collect(ownDenots, classParents) - } else NoDenotation - } - - override final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = { - val raw = if (excluded is Private) nonPrivateMembersNamed(name) else membersNamed(name) - raw.filterExcluded(excluded).asSeenFrom(pre).toDenot(pre) - } - - private[this] var baseTypeRefCache: java.util.HashMap[CachedType, Type] = null - private[this] var baseTypeRefValid: RunId = NoRunId - - /** Compute tp.baseTypeRef(this) */ - final def baseTypeRefOf(tp: Type)(implicit ctx: Context): Type = { - - def foldGlb(bt: Type, ps: List[Type]): Type = ps match { - case p :: ps1 => foldGlb(bt & baseTypeRefOf(p), ps1) - case _ => bt - } - - def inCache(tp: Type) = baseTypeRefCache.containsKey(tp) - - /** We cannot cache: - * - type variables which are uninstantiated or whose instances can - * change, depending on typerstate. - * - types where the underlying type is an ErasedValueType, because - * this underlying type will change after ElimErasedValueType, - * and this changes subtyping relations. As a shortcut, we do not - * cache ErasedValueType at all. - */ - def isCachable(tp: Type): Boolean = tp match { - case _: TypeErasure.ErasedValueType => false - case tp: TypeRef if tp.symbol.isClass => true - case tp: TypeVar => tp.inst.exists && inCache(tp.inst) - case tp: TypeProxy => inCache(tp.underlying) - case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) - case _ => true - } - - def computeBaseTypeRefOf(tp: Type): Type = { - Stats.record("computeBaseTypeOf") - if (symbol.isStatic && tp.derivesFrom(symbol)) - symbol.typeRef - else tp match { - case tp: TypeRef => - val subcls = tp.symbol - if (subcls eq symbol) - tp - else subcls.denot match { - case cdenot: ClassDenotation => - if (cdenot.superClassBits contains symbol.superId) foldGlb(NoType, tp.parents) - else NoType - case _ => - baseTypeRefOf(tp.superType) - } - case tp: TypeProxy => - baseTypeRefOf(tp.superType) - case AndType(tp1, tp2) => - baseTypeRefOf(tp1) & baseTypeRefOf(tp2) - case OrType(tp1, tp2) => - baseTypeRefOf(tp1) | baseTypeRefOf(tp2) - case JavaArrayType(_) if symbol == defn.ObjectClass => - this.typeRef - case _ => - NoType - } - } - - /*>|>*/ ctx.debugTraceIndented(s"$tp.baseTypeRef($this)") /*<|<*/ { - tp match { - case tp: CachedType => - checkBasesUpToDate() - var basetp = baseTypeRefCache get tp - if (basetp == null) { - baseTypeRefCache.put(tp, NoPrefix) - basetp = computeBaseTypeRefOf(tp) - if (isCachable(tp)) baseTypeRefCache.put(tp, basetp) - else baseTypeRefCache.remove(tp) - } else if (basetp == NoPrefix) { - baseTypeRefCache.put(tp, null) - throw CyclicReference(this) - } - basetp - case _ => - computeBaseTypeRefOf(tp) - } - } - } - - private[this] var memberNamesCache: SimpleMap[NameFilter, Set[Name]] = SimpleMap.Empty - - def memberNames(keepOnly: NameFilter)(implicit ctx: Context): Set[Name] = { - def computeMemberNames: Set[Name] = { - var names = Set[Name]() - def maybeAdd(name: Name) = if (keepOnly(thisType, name)) names += name - for (p <- classParents) - for (name <- p.memberNames(keepOnly, thisType)) maybeAdd(name) - val ownSyms = - if (keepOnly == implicitFilter) - if (this is Package) Iterator.empty - else info.decls.iterator filter (_ is Implicit) - else info.decls.iterator - for (sym <- ownSyms) maybeAdd(sym.name) - names - } - if ((this is PackageClass) || !Config.cacheMemberNames) - computeMemberNames // don't cache package member names; they might change - else { - val cached = memberNamesCache(keepOnly) - if (cached != null) cached - else { - val names = computeMemberNames - if (isFullyCompleted) { - setFlag(Frozen) - memberNamesCache = memberNamesCache.updated(keepOnly, names) - } - names - } - } - } - - private[this] var fullNameCache: SimpleMap[String, Name] = SimpleMap.Empty - override final def fullNameSeparated(separator: String)(implicit ctx: Context): Name = { - val cached = fullNameCache(separator) - if (cached != null) cached - else { - val fn = super.fullNameSeparated(separator) - fullNameCache = fullNameCache.updated(separator, fn) - fn - } - } - - // to avoid overloading ambiguities - override def fullName(implicit ctx: Context): Name = super.fullName - - override def primaryConstructor(implicit ctx: Context): Symbol = { - def constrNamed(cname: TermName) = info.decls.denotsNamed(cname).last.symbol - // denotsNamed returns Symbols in reverse order of occurrence - if (this.is(ImplClass)) constrNamed(nme.TRAIT_CONSTRUCTOR) // ignore normal constructor - else - constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR)) - } - - /** The parameter accessors of this class. Term and type accessors, - * getters and setters are all returned int his list - */ - def paramAccessors(implicit ctx: Context): List[Symbol] = - unforcedDecls.filter(_ is ParamAccessor).toList - - /** If this class has the same `decls` scope reference in `phase` and - * `phase.next`, install a new denotation with a cloned scope in `phase.next`. - */ - def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = - if (ctx.phaseId != phase.next.id) ensureFreshScopeAfter(phase)(ctx.withPhase(phase.next)) - else { - val prevCtx = ctx.withPhase(phase) - val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo - if (classInfo(prevCtx).decls eq decls) - copySymDenotation(info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo)) - .installAfter(phase) - } - } - - /** The denotation of a package class. - * It overrides ClassDenotation to take account of package objects when looking for members - */ - class PackageClassDenotation private[SymDenotations] ( - symbol: Symbol, - ownerIfExists: Symbol, - name: Name, - initFlags: FlagSet, - initInfo: Type, - initPrivateWithin: Symbol, - initRunId: RunId) - extends ClassDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin, initRunId) { - - private[this] var packageObjCache: SymDenotation = _ - private[this] var packageObjRunId: RunId = NoRunId - - /** The package object in this class, of one exists */ - def packageObj(implicit ctx: Context): SymDenotation = { - if (packageObjRunId != ctx.runId) { - packageObjRunId = ctx.runId - packageObjCache = NoDenotation // break cycle in case we are looking for package object itself - packageObjCache = findMember(nme.PACKAGE, thisType, EmptyFlags).asSymDenotation - } - packageObjCache - } - - /** Look first for members in package; if none are found look in package object */ - override def computeNPMembersNamed(name: Name, inherited: Boolean)(implicit ctx: Context): PreDenotation = { - val denots = super.computeNPMembersNamed(name, inherited) - if (denots.exists) denots - else packageObj.moduleClass.denot match { - case pcls: ClassDenotation => pcls.computeNPMembersNamed(name, inherited) - case _ => denots - } - } - - /** The union of the member names of the package and the package object */ - override def memberNames(keepOnly: NameFilter)(implicit ctx: Context): Set[Name] = { - val ownNames = super.memberNames(keepOnly) - packageObj.moduleClass.denot match { - case pcls: ClassDenotation => ownNames union pcls.memberNames(keepOnly) - case _ => ownNames - } - } - } - - class NoDenotation extends SymDenotation( - NoSymbol, NoSymbol, "<none>".toTermName, Permanent, NoType) { - override def exists = false - override def isTerm = false - override def isType = false - override def owner: Symbol = throw new AssertionError("NoDenotation.owner") - override def computeAsSeenFrom(pre: Type)(implicit ctx: Context): SingleDenotation = this - override def mapInfo(f: Type => Type)(implicit ctx: Context): SingleDenotation = this - validFor = Period.allInRun(NoRunId) // will be brought forward automatically - } - - @sharable val NoDenotation = new NoDenotation - - // ---- Completion -------------------------------------------------------- - - /** Instances of LazyType are carried by uncompleted symbols. - * Note: LazyTypes double up as (constant) functions from Symbol and - * from (TermSymbol, ClassSymbol) to LazyType. That way lazy types can be - * directly passed to symbol creation methods in Symbols that demand instances - * of these function types. - */ - abstract class LazyType extends UncachedGroundType - with (Symbol => LazyType) - with ((TermSymbol, ClassSymbol) => LazyType) { self => - - /** Sets all missing fields of given denotation */ - def complete(denot: SymDenotation)(implicit ctx: Context): Unit - - def apply(sym: Symbol) = this - def apply(module: TermSymbol, modcls: ClassSymbol) = this - - private var myDecls: Scope = EmptyScope - private var mySourceModuleFn: Context => Symbol = NoSymbolFn - private var myModuleClassFn: Context => Symbol = NoSymbolFn - - /** A proxy to this lazy type that keeps the complete operation - * but provides fresh slots for scope/sourceModule/moduleClass - */ - def proxy: LazyType = new LazyType { - override def complete(denot: SymDenotation)(implicit ctx: Context) = self.complete(denot) - } - - def decls: Scope = myDecls - def sourceModule(implicit ctx: Context): Symbol = mySourceModuleFn(ctx) - def moduleClass(implicit ctx: Context): Symbol = myModuleClassFn(ctx) - - def withDecls(decls: Scope): this.type = { myDecls = decls; this } - def withSourceModule(sourceModuleFn: Context => Symbol): this.type = { mySourceModuleFn = sourceModuleFn; this } - def withModuleClass(moduleClassFn: Context => Symbol): this.type = { myModuleClassFn = moduleClassFn; this } - } - - /** A subclass of LazyTypes where type parameters can be completed independently of - * the info. - */ - trait TypeParamsCompleter extends LazyType { - /** The type parameters computed by the completer before completion has finished */ - def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] - } - - val NoSymbolFn = (ctx: Context) => NoSymbol - - /** A missing completer */ - @sharable class NoCompleter extends LazyType { - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = unsupported("complete") - } - - object NoCompleter extends NoCompleter - - /** A lazy type for modules that points to the module class. - * Needed so that `moduleClass` works before completion. - * Completion of modules is always completion of the underlying - * module class, followed by copying the relevant fields to the module. - */ - class ModuleCompleter(_moduleClass: ClassSymbol) extends LazyType { - override def moduleClass(implicit ctx: Context) = _moduleClass - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val from = moduleClass.denot.asClass - denot.setFlag(from.flags.toTermFlags & RetainedModuleValFlags) - denot.annotations = from.annotations filter (_.appliesToModule) - // !!! ^^^ needs to be revised later. The problem is that annotations might - // only apply to the module but not to the module class. The right solution - // is to have the module class completer set the annotations of both the - // class and the module. - denot.info = moduleClass.typeRef - denot.privateWithin = from.privateWithin - } - } - - /** A completer for missing references */ - class StubInfo() extends LazyType { - - def initializeToDefaults(denot: SymDenotation)(implicit ctx: Context) = { - denot.info = denot match { - case denot: ClassDenotation => - ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, EmptyScope) - case _ => - ErrorType - } - denot.privateWithin = NoSymbol - } - - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val sym = denot.symbol - val file = sym.associatedFile - val (location, src) = - if (file != null) (s" in $file", file.toString) - else ("", "the signature") - val name = ctx.fresh.setSetting(ctx.settings.debugNames, true).nameString(denot.name) - ctx.error( - i"""bad symbolic reference. A signature$location - |refers to $name in ${denot.owner.showKind} ${denot.owner.showFullName} which is not available. - |It may be completely missing from the current classpath, or the version on - |the classpath might be incompatible with the version used when compiling $src.""") - if (ctx.debug) throw new Error() - initializeToDefaults(denot) - } - } - - // ---- Fingerprints ----------------------------------------------------- - - /** A fingerprint is a bitset that acts as a bloom filter for sets - * of names. - */ - class FingerPrint(val bits: Array[Long]) extends AnyVal { - import FingerPrint._ - - /** Include some bits of name's hashcode in set */ - def include(name: Name): Unit = { - val hash = name.hashCode & Mask - bits(hash >> WordSizeLog) |= (1L << hash) - } - - /** Include all bits of `that` fingerprint in set */ - def include(that: FingerPrint): Unit = - for (i <- 0 until NumWords) bits(i) |= that.bits(i) - - /** Does set contain hash bits of given name? */ - def contains(name: Name): Boolean = { - val hash = name.hashCode & Mask - (bits(hash >> WordSizeLog) & (1L << hash)) != 0 - } - } - - object FingerPrint { - def apply() = new FingerPrint(new Array[Long](NumWords)) - val unknown = new FingerPrint(null) - private final val WordSizeLog = 6 - private final val NumWords = 32 - private final val NumBits = NumWords << WordSizeLog - private final val Mask = NumBits - 1 - } - - private val AccessorOrLabel = Accessor | Label - - @sharable private var indent = 0 // for completions printing -} diff --git a/src/dotty/tools/dotc/core/SymbolLoaders.scala b/src/dotty/tools/dotc/core/SymbolLoaders.scala deleted file mode 100644 index 4ae28c10b..000000000 --- a/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ /dev/null @@ -1,267 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Martin Odersky - */ - -package dotty.tools -package dotc -package core - -import java.io.IOException -import scala.compat.Platform.currentTime -import dotty.tools.io.{ ClassPath, AbstractFile } -import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, util.Positions._, Names._ -import StdNames._, NameOps._ -import Decorators.{StringDecorator, StringInterpolators} -import classfile.ClassfileParser -import scala.util.control.NonFatal - -object SymbolLoaders { - /** A marker trait for a completer that replaces the original - * Symbol loader for an unpickled root. - */ - trait SecondCompleter -} - -/** A base class for Symbol loaders with some overridable behavior */ -class SymbolLoaders { - - protected def enterNew( - owner: Symbol, member: Symbol, - completer: SymbolLoader, scope: Scope = EmptyScope)(implicit ctx: Context): Symbol = { - assert(scope.lookup(member.name) == NoSymbol, s"${owner.fullName}.${member.name} already has a symbol") - owner.asClass.enter(member, scope) - member - } - - /** Enter class with given `name` into scope of `owner`. - */ - def enterClass( - owner: Symbol, name: PreName, completer: SymbolLoader, - flags: FlagSet = EmptyFlags, scope: Scope = EmptyScope)(implicit ctx: Context): Symbol = { - val cls = ctx.newClassSymbol(owner, name.toTypeName, flags, completer, assocFile = completer.sourceFileOrNull) - enterNew(owner, cls, completer, scope) - } - - /** Enter module with given `name` into scope of `owner`. - */ - def enterModule( - owner: Symbol, name: PreName, completer: SymbolLoader, - modFlags: FlagSet = EmptyFlags, clsFlags: FlagSet = EmptyFlags, scope: Scope = EmptyScope)(implicit ctx: Context): Symbol = { - val module = ctx.newModuleSymbol( - owner, name.toTermName, modFlags, clsFlags, - (module, _) => completer.proxy withDecls newScope withSourceModule (_ => module), - assocFile = completer.sourceFileOrNull) - enterNew(owner, module, completer, scope) - enterNew(owner, module.moduleClass, completer, scope) - } - - /** Enter package with given `name` into scope of `owner` - * and give them `completer` as type. - */ - def enterPackage(owner: Symbol, pkg: ClassPath)(implicit ctx: Context): Symbol = { - val pname = pkg.name.toTermName - val preExisting = owner.info.decls lookup pname - if (preExisting != NoSymbol) { - // Some jars (often, obfuscated ones) include a package and - // object with the same name. Rather than render them unusable, - // offer a setting to resolve the conflict one way or the other. - // This was motivated by the desire to use YourKit probes, which - // require yjp.jar at runtime. See SI-2089. - if (ctx.settings.termConflict.isDefault) - throw new TypeError( - i"""$owner contains object and package with same name: $pname - |one of them needs to be removed from classpath""") - else if (ctx.settings.termConflict.value == "package") { - ctx.warning( - s"Resolving package/object name conflict in favor of package ${preExisting.fullName}. The object will be inaccessible.") - owner.asClass.delete(preExisting) - } else { - ctx.warning( - s"Resolving package/object name conflict in favor of object ${preExisting.fullName}. The package will be inaccessible.") - return NoSymbol - } - } - ctx.newModuleSymbol(owner, pname, PackageCreationFlags, PackageCreationFlags, - (module, modcls) => new PackageLoader(module, pkg)).entered - } - - /** Enter class and module with given `name` into scope of `owner` - * and give them `completer` as type. - */ - def enterClassAndModule( - owner: Symbol, name: PreName, completer: SymbolLoader, - flags: FlagSet = EmptyFlags, scope: Scope = EmptyScope)(implicit ctx: Context): Unit = { - val clazz = enterClass(owner, name, completer, flags, scope) - val module = enterModule( - owner, name, completer, - modFlags = flags.toTermFlags & RetainedModuleValFlags, - clsFlags = flags.toTypeFlags & RetainedModuleClassFlags, - scope = scope) - } - - /** In batch mode: Enter class and module with given `name` into scope of `owner` - * and give them a source completer for given `src` as type. - * In IDE mode: Find all toplevel definitions in `src` and enter then into scope of `owner` - * with source completer for given `src` as type. - * (overridden in interactive.Global). - */ - def enterToplevelsFromSource( - owner: Symbol, name: PreName, src: AbstractFile, - scope: Scope = EmptyScope)(implicit ctx: Context): Unit = { - enterClassAndModule(owner, name, new SourcefileLoader(src), scope = scope) - } - - /** The package objects of scala and scala.reflect should always - * be loaded in binary if classfiles are available, even if sourcefiles - * are newer. Late-compiling these objects from source leads to compilation - * order issues. - * Note: We do a name-base comparison here because the method is called before we even - * have ReflectPackage defined. - */ - def binaryOnly(owner: Symbol, name: String)(implicit ctx: Context): Boolean = - name == "package" && - (owner.fullName.toString == "scala" || owner.fullName.toString == "scala.reflect") - - /** Initialize toplevel class and module symbols in `owner` from class path representation `classRep` - */ - def initializeFromClassPath(owner: Symbol, classRep: ClassPath#ClassRep)(implicit ctx: Context): Unit = { - ((classRep.binary, classRep.source): @unchecked) match { - case (Some(bin), Some(src)) if needCompile(bin, src) && !binaryOnly(owner, classRep.name) => - if (ctx.settings.verbose.value) ctx.inform("[symloader] picked up newer source file for " + src.path) - enterToplevelsFromSource(owner, classRep.name, src) - case (None, Some(src)) => - if (ctx.settings.verbose.value) ctx.inform("[symloader] no class, picked up source file for " + src.path) - enterToplevelsFromSource(owner, classRep.name, src) - case (Some(bin), _) => - enterClassAndModule(owner, classRep.name, ctx.platform.newClassLoader(bin)) - } - } - - def needCompile(bin: AbstractFile, src: AbstractFile) = - src.lastModified >= bin.lastModified - - /** Load contents of a package - */ - class PackageLoader(_sourceModule: TermSymbol, classpath: ClassPath) - extends SymbolLoader { - override def sourceModule(implicit ctx: Context) = _sourceModule - def description = "package loader " + classpath.name - - private[core] val currentDecls: MutableScope = newScope - - def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = { - assert(root is PackageClass, root) - def maybeModuleClass(classRep: ClassPath#ClassRep) = classRep.name.last == '$' - val pre = root.owner.thisType - root.info = ClassInfo(pre, root.symbol.asClass, Nil, currentDecls, pre select sourceModule) - if (!sourceModule.isCompleted) - sourceModule.completer.complete(sourceModule) - if (!root.isRoot) { - for (classRep <- classpath.classes) - if (!maybeModuleClass(classRep)) - initializeFromClassPath(root.symbol, classRep) - for (classRep <- classpath.classes) - if (maybeModuleClass(classRep) && !root.unforcedDecls.lookup(classRep.name.toTypeName).exists) - initializeFromClassPath(root.symbol, classRep) - } - if (!root.isEmptyPackage) - for (pkg <- classpath.packages) - enterPackage(root.symbol, pkg) - } - } -} - -/** A lazy type that completes itself by calling parameter doComplete. - * Any linked modules/classes or module classes are also initialized. - */ -abstract class SymbolLoader extends LazyType { - - /** Load source or class file for `root`, return */ - def doComplete(root: SymDenotation)(implicit ctx: Context): Unit - - def sourceFileOrNull: AbstractFile = null - - /** Description of the resource (ClassPath, AbstractFile) - * being processed by this loader - */ - def description: String - - override def complete(root: SymDenotation)(implicit ctx: Context): Unit = { - def signalError(ex: Exception): Unit = { - if (ctx.debug) ex.printStackTrace() - val msg = ex.getMessage() - ctx.error( - if (msg eq null) "i/o error while loading " + root.name - else "error while loading " + root.name + ",\n " + msg) - } - try { - val start = currentTime - if (ctx.settings.debugTrace.value) - ctx.doTraceIndented(s">>>> loading ${root.debugString}", _ => s"<<<< loaded ${root.debugString}") { - doComplete(root) - } - else - doComplete(root) - ctx.informTime("loaded " + description, start) - } catch { - case ex: IOException => - signalError(ex) - case NonFatal(ex) => - println(s"exception caught when loading $root: $ex") - throw ex - } finally { - def postProcess(denot: SymDenotation) = - if (!denot.isCompleted && - !denot.completer.isInstanceOf[SymbolLoaders.SecondCompleter]) - denot.markAbsent() - postProcess(root) - if (!root.isRoot) - postProcess(root.scalacLinkedClass.denot) - } - } -} - -class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader { - - override def sourceFileOrNull: AbstractFile = classfile - - def description = "class file " + classfile.toString - - def rootDenots(rootDenot: ClassDenotation)(implicit ctx: Context): (ClassDenotation, ClassDenotation) = { - val linkedDenot = rootDenot.scalacLinkedClass.denot match { - case d: ClassDenotation => d - case d => - // this can happen if the companion if shadowed by a val or type - // in a package object; in this case, we make up some dummy denotation - // as a stand in for loading. - // An example for this situation is scala.reflect.Manifest, which exists - // as a class in scala.reflect and as a val in scala.reflect.package. - if (rootDenot is ModuleClass) - ctx.newClassSymbol( - rootDenot.owner, rootDenot.name.stripModuleClassSuffix.asTypeName, Synthetic, - _ => NoType).classDenot - else - ctx.newModuleSymbol( - rootDenot.owner, rootDenot.name.toTermName, Synthetic, Synthetic, - (module, _) => new NoCompleter() withDecls newScope withSourceModule (_ => module)) - .moduleClass.denot.asClass - } - if (rootDenot is ModuleClass) (linkedDenot, rootDenot) - else (rootDenot, linkedDenot) - } - - override def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = - load(root) - - def load(root: SymDenotation)(implicit ctx: Context): Option[ClassfileParser.Embedded] = { - val (classRoot, moduleRoot) = rootDenots(root.asClass) - new ClassfileParser(classfile, classRoot, moduleRoot)(ctx).run() - } -} - -class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader { - def description = "source file " + srcfile.toString - override def sourceFileOrNull = srcfile - def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = unsupported("doComplete") -} diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala deleted file mode 100644 index b5bd196d2..000000000 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ /dev/null @@ -1,602 +0,0 @@ -package dotty.tools -package dotc -package core - -import Periods._ -import Names._ -import Scopes._ -import Flags._ -import java.lang.AssertionError -import Decorators._ -import Symbols._ -import Contexts._ -import SymDenotations._ -import printing.Texts._ -import printing.Printer -import Types._ -import Annotations._ -import util.Positions._ -import DenotTransformers._ -import StdNames._ -import NameOps._ -import ast.tpd.Tree -import ast.TreeTypeMap -import Constants.Constant -import Denotations.{ Denotation, SingleDenotation, MultiDenotation } -import collection.mutable -import io.AbstractFile -import language.implicitConversions -import util.{NoSource, DotClass} - -/** Creation methods for symbols */ -trait Symbols { this: Context => - -// ---- Factory methods for symbol creation ---------------------- -// -// All symbol creations should be done via the next two methods. - - /** Create a symbol without a denotation. - * Note this uses a cast instead of a direct type refinement because - * it's debug-friendlier not to create an anonymous class here. - */ - def newNakedSymbol[N <: Name](coord: Coord = NoCoord)(implicit ctx: Context): Symbol { type ThisName = N } = - new Symbol(coord, ctx.nextId).asInstanceOf[Symbol { type ThisName = N }] - - /** Create a class symbol without a denotation. */ - def newNakedClassSymbol(coord: Coord = NoCoord, assocFile: AbstractFile = null)(implicit ctx: Context) = - new ClassSymbol(coord, assocFile, ctx.nextId) - -// ---- Symbol creation methods ---------------------------------- - - /** Create a symbol from its fields (info may be lazy) */ - def newSymbol[N <: Name]( - owner: Symbol, - name: N, - flags: FlagSet, - info: Type, - privateWithin: Symbol = NoSymbol, - coord: Coord = NoCoord): Symbol { type ThisName = N } = { - val sym = newNakedSymbol[N](coord) - val denot = SymDenotation(sym, owner, name, flags, info, privateWithin) - sym.denot = denot - sym - } - - /** Create a class symbol from a function producing its denotation */ - def newClassSymbolDenoting(denotFn: ClassSymbol => SymDenotation, coord: Coord = NoCoord, assocFile: AbstractFile = null): ClassSymbol = { - val cls = newNakedClassSymbol(coord, assocFile) - cls.denot = denotFn(cls) - cls - } - - /** Create a class symbol from its non-info fields and a function - * producing its info (the produced info may be lazy). - */ - def newClassSymbol( - owner: Symbol, - name: TypeName, - flags: FlagSet, - infoFn: ClassSymbol => Type, - privateWithin: Symbol = NoSymbol, - coord: Coord = NoCoord, - assocFile: AbstractFile = null): ClassSymbol - = { - val cls = newNakedClassSymbol(coord, assocFile) - val denot = SymDenotation(cls, owner, name, flags, infoFn(cls), privateWithin) - cls.denot = denot - cls - } - - /** Create a class symbol from its non-info fields and the fields of its info. */ - def newCompleteClassSymbol( - owner: Symbol, - name: TypeName, - flags: FlagSet, - parents: List[TypeRef], - decls: Scope = newScope, - selfInfo: Type = NoType, - privateWithin: Symbol = NoSymbol, - coord: Coord = NoCoord, - assocFile: AbstractFile = null): ClassSymbol = - newClassSymbol( - owner, name, flags, - ClassInfo(owner.thisType, _, parents, decls, selfInfo), - privateWithin, coord, assocFile) - - /** Same as `newCompleteClassSymbol` except that `parents` can be a list of arbitrary - * types which get normalized into type refs and parameter bindings. - */ - def newNormalizedClassSymbol( - owner: Symbol, - name: TypeName, - flags: FlagSet, - parentTypes: List[Type], - decls: Scope = newScope, - selfInfo: Type = NoType, - privateWithin: Symbol = NoSymbol, - coord: Coord = NoCoord, - assocFile: AbstractFile = null): ClassSymbol = { - def completer = new LazyType { - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val cls = denot.asClass.classSymbol - val decls = newScope - val parentRefs: List[TypeRef] = normalizeToClassRefs(parentTypes, cls, decls) - denot.info = ClassInfo(owner.thisType, cls, parentRefs, decls) - } - } - newClassSymbol(owner, name, flags, completer, privateWithin, coord, assocFile) - } - - /** Create a module symbol with associated module class - * from its non-info fields and a function producing the info - * of the module class (this info may be lazy). - */ - def newModuleSymbol( - owner: Symbol, - name: TermName, - modFlags: FlagSet, - clsFlags: FlagSet, - infoFn: (TermSymbol, ClassSymbol) => Type, // typically a ModuleClassCompleterWithDecls - privateWithin: Symbol = NoSymbol, - coord: Coord = NoCoord, - assocFile: AbstractFile = null): TermSymbol - = { - val base = owner.thisType - val module = newNakedSymbol[TermName](coord) - val modcls = newNakedClassSymbol(coord, assocFile) - val modclsFlags = clsFlags | ModuleClassCreationFlags - val modclsName = name.toTypeName.adjustIfModuleClass(modclsFlags) - val cdenot = SymDenotation( - modcls, owner, modclsName, modclsFlags, - infoFn(module, modcls), privateWithin) - val mdenot = SymDenotation( - module, owner, name, modFlags | ModuleCreationFlags, - if (cdenot.isCompleted) TypeRef.withSymAndName(owner.thisType, modcls, modclsName) - else new ModuleCompleter(modcls)) - module.denot = mdenot - modcls.denot = cdenot - module - } - - /** Create a module symbol with associated module class - * from its non-info fields and the fields of the module class info. - * @param flags The combined flags of the module and the module class - * These are masked with RetainedModuleValFlags/RetainedModuleClassFlags. - */ - def newCompleteModuleSymbol( - owner: Symbol, - name: TermName, - modFlags: FlagSet, - clsFlags: FlagSet, - parents: List[TypeRef], - decls: Scope, - privateWithin: Symbol = NoSymbol, - coord: Coord = NoCoord, - assocFile: AbstractFile = null): TermSymbol = - newModuleSymbol( - owner, name, modFlags, clsFlags, - (module, modcls) => ClassInfo( - owner.thisType, modcls, parents, decls, TermRef.withSymAndName(owner.thisType, module, name)), - privateWithin, coord, assocFile) - - val companionMethodFlags = Flags.Synthetic | Flags.Private | Flags.Method - - def synthesizeCompanionMethod(name: Name, target: SymDenotation, owner: SymDenotation)(implicit ctx: Context) = - if (owner.exists && target.exists && !owner.isAbsent && !target.isAbsent) { - val existing = owner.unforcedDecls.lookup(name) - - existing.orElse{ - ctx.newSymbol(owner.symbol, name, companionMethodFlags , ExprType(target.typeRef)) - } - } else NoSymbol - - /** Create a package symbol with associated package class - * from its non-info fields and a lazy type for loading the package's members. - */ - def newPackageSymbol( - owner: Symbol, - name: TermName, - infoFn: (TermSymbol, ClassSymbol) => LazyType): TermSymbol = - newModuleSymbol(owner, name, PackageCreationFlags, PackageCreationFlags, infoFn) - - /** Create a package symbol with associated package class - * from its non-info fields its member scope. - */ - def newCompletePackageSymbol( - owner: Symbol, - name: TermName, - modFlags: FlagSet = EmptyFlags, - clsFlags: FlagSet = EmptyFlags, - decls: Scope = newScope): TermSymbol = - newCompleteModuleSymbol( - owner, name, - modFlags | PackageCreationFlags, clsFlags | PackageCreationFlags, - Nil, decls) - - - /** Create a stub symbol that will issue a missing reference error - * when attempted to be completed. - */ - def newStubSymbol(owner: Symbol, name: Name, file: AbstractFile = null): Symbol = { - def stubCompleter = new StubInfo() - val normalizedOwner = if (owner is ModuleVal) owner.moduleClass else owner - println(s"creating stub for ${name.show}, owner = ${normalizedOwner.denot.debugString}, file = $file") - println(s"decls = ${normalizedOwner.unforcedDecls.toList.map(_.debugString).mkString("\n ")}") // !!! DEBUG - //if (base.settings.debug.value) throw new Error() - val stub = name match { - case name: TermName => - newModuleSymbol(normalizedOwner, name, EmptyFlags, EmptyFlags, stubCompleter, assocFile = file) - case name: TypeName => - newClassSymbol(normalizedOwner, name, EmptyFlags, stubCompleter, assocFile = file) - } - stubs = stub :: stubs - stub - } - - /** Create the local template dummy of given class `cls`. - * In a template - * - * trait T { val fld: Int; { val x: int = 2 }; val fld2 = { val y = 2; y }} - * - * the owner of `x` is the local dummy of the template. The owner of the local - * dummy is then the class of the template itself. By contrast, the owner of `y` - * would be `fld2`. There is a single local dummy per template. - */ - def newLocalDummy(cls: Symbol, coord: Coord = NoCoord) = - newSymbol(cls, nme.localDummyName(cls), EmptyFlags, NoType) - - /** Create an import symbol pointing back to given qualifier `expr`. */ - def newImportSymbol(owner: Symbol, expr: Tree, coord: Coord = NoCoord) = - newSymbol(owner, nme.IMPORT, EmptyFlags, ImportType(expr), coord = coord) - - /** Create a class constructor symbol for given class `cls`. */ - def newConstructor(cls: ClassSymbol, flags: FlagSet, paramNames: List[TermName], paramTypes: List[Type], privateWithin: Symbol = NoSymbol, coord: Coord = NoCoord) = - newSymbol(cls, nme.CONSTRUCTOR, flags | Method, MethodType(paramNames, paramTypes)(_ => cls.typeRef), privateWithin, coord) - - /** Create an empty default constructor symbol for given class `cls`. */ - def newDefaultConstructor(cls: ClassSymbol) = - newConstructor(cls, EmptyFlags, Nil, Nil) - - /** Create a symbol representing a selftype declaration for class `cls`. */ - def newSelfSym(cls: ClassSymbol, name: TermName = nme.WILDCARD, selfInfo: Type = NoType): TermSymbol = - ctx.newSymbol(cls, name, SelfSymFlags, selfInfo orElse cls.classInfo.selfType, coord = cls.coord) - - /** Create new type parameters with given owner, names, and flags. - * @param boundsFn A function that, given type refs to the newly created - * parameters returns a list of their bounds. - */ - def newTypeParams( - owner: Symbol, - names: List[TypeName], - flags: FlagSet, - boundsFn: List[TypeRef] => List[Type]): List[TypeSymbol] = { - - val tparamBuf = new mutable.ListBuffer[TypeSymbol] - val trefBuf = new mutable.ListBuffer[TypeRef] - for (name <- names) { - val tparam = newNakedSymbol[TypeName](NoCoord) - tparamBuf += tparam - trefBuf += TypeRef.withSymAndName(owner.thisType, tparam, name) - } - val tparams = tparamBuf.toList - val bounds = boundsFn(trefBuf.toList) - for ((name, tparam, bound) <- (names, tparams, bounds).zipped) - tparam.denot = SymDenotation(tparam, owner, name, flags | owner.typeParamCreationFlags, bound) - tparams - } - - /** Create a new skolem symbol. This is not the same as SkolemType, even though the - * motivation (create a singleton referencing to a type) is similar. - */ - def newSkolem(tp: Type) = newSymbol(defn.RootClass, nme.SKOLEM, SyntheticArtifact | Permanent, tp) - - def newErrorSymbol(owner: Symbol, name: Name) = - newSymbol(owner, name, SyntheticArtifact, - if (name.isTypeName) TypeAlias(ErrorType) else ErrorType) - - /** Map given symbols, subjecting their attributes to the mappings - * defined in the given TreeTypeMap `ttmap`. - * Cross symbol references are brought over from originals to copies. - * Do not copy any symbols if all attributes of all symbols stay the same. - */ - def mapSymbols(originals: List[Symbol], ttmap: TreeTypeMap, mapAlways: Boolean = false): List[Symbol] = - if (originals.forall(sym => - (ttmap.mapType(sym.info) eq sym.info) && - !(ttmap.oldOwners contains sym.owner)) && !mapAlways) - originals - else { - val copies: List[Symbol] = for (original <- originals) yield - original match { - case original: ClassSymbol => - newNakedClassSymbol(original.coord, original.assocFile) - case _ => - newNakedSymbol[original.ThisName](original.coord) - } - val ttmap1 = ttmap.withSubstitution(originals, copies) - (originals, copies).zipped foreach {(original, copy) => - copy.denot = original.denot // preliminary denotation, so that we can access symbols in subsequent transform - } - (originals, copies).zipped foreach {(original, copy) => - val odenot = original.denot - val oinfo = original.info match { - case ClassInfo(pre, _, parents, decls, selfInfo) => - assert(original.isClass) - ClassInfo(pre, copy.asClass, parents, decls.cloneScope, selfInfo) - case oinfo => oinfo - } - copy.denot = odenot.copySymDenotation( - symbol = copy, - owner = ttmap1.mapOwner(odenot.owner), - initFlags = odenot.flags &~ Frozen | Fresh, - info = ttmap1.mapType(oinfo), - privateWithin = ttmap1.mapOwner(odenot.privateWithin), // since this refers to outer symbols, need not include copies (from->to) in ownermap here. - annotations = odenot.annotations.mapConserve(ttmap1.apply)) - } - copies - } - -// ----- Locating predefined symbols ---------------------------------------- - - def requiredPackage(path: PreName): TermSymbol = - base.staticRef(path.toTermName).requiredSymbol(_ is Package).asTerm - - def requiredPackageRef(path: PreName): TermRef = requiredPackage(path).termRef - - def requiredClass(path: PreName): ClassSymbol = - base.staticRef(path.toTypeName).requiredSymbol(_.isClass).asClass - - def requiredClassRef(path: PreName): TypeRef = requiredClass(path).typeRef - - /** Get ClassSymbol if class is either defined in current compilation run - * or present on classpath. - * Returns NoSymbol otherwise. */ - def getClassIfDefined(path: PreName): Symbol = - base.staticRef(path.toTypeName, generateStubs = false).requiredSymbol(_.isClass, generateStubs = false) - - def requiredModule(path: PreName): TermSymbol = - base.staticRef(path.toTermName).requiredSymbol(_ is Module).asTerm - - def requiredModuleRef(path: PreName): TermRef = requiredModule(path).termRef -} - -object Symbols { - - implicit def eqSymbol: Eq[Symbol, Symbol] = Eq - - /** A Symbol represents a Scala definition/declaration or a package. - * @param coord The coordinates of the symbol (a position or an index) - * @param id A unique identifier of the symbol (unique per ContextBase) - */ - class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with TypeParamInfo with printing.Showable { - - type ThisName <: Name - - //assert(id != 4285) - - /** The last denotation of this symbol */ - private[this] var lastDenot: SymDenotation = _ - - /** Set the denotation of this symbol */ - private[core] def denot_=(d: SymDenotation) = - lastDenot = d - - /** The current denotation of this symbol */ - final def denot(implicit ctx: Context): SymDenotation = { - var denot = lastDenot - if (!(denot.validFor contains ctx.period)) { - denot = denot.current.asInstanceOf[SymDenotation] - lastDenot = denot - } - denot - } - - private[core] def defRunId: RunId = - if (lastDenot == null) NoRunId else lastDenot.validFor.runId - - /** Does this symbol come from a currently compiled source file? */ - final def isDefinedInCurrentRun(implicit ctx: Context): Boolean = { - pos.exists && defRunId == ctx.runId - } - - /** Subclass tests and casts */ - final def isTerm(implicit ctx: Context): Boolean = - (if (defRunId == ctx.runId) lastDenot else denot).isTerm - - final def isType(implicit ctx: Context): Boolean = - (if (defRunId == ctx.runId) lastDenot else denot).isType - - final def isClass: Boolean = isInstanceOf[ClassSymbol] - - final def asTerm(implicit ctx: Context): TermSymbol = { assert(isTerm, s"asTerm called on not-a-Term $this" ); asInstanceOf[TermSymbol] } - final def asType(implicit ctx: Context): TypeSymbol = { assert(isType, s"isType called on not-a-Type $this"); asInstanceOf[TypeSymbol] } - final def asClass: ClassSymbol = asInstanceOf[ClassSymbol] - - final def isFresh(implicit ctx: Context) = - lastDenot != null && (lastDenot is Fresh) - - /** Special cased here, because it may be used on naked symbols in substituters */ - final def isStatic(implicit ctx: Context): Boolean = - lastDenot != null && denot.isStatic - - /** A unique, densely packed integer tag for each class symbol, -1 - * for all other symbols. To save memory, this method - * should be called only if class is a super class of some other class. - */ - def superId(implicit ctx: Context): Int = -1 - - /** This symbol entered into owner's scope (owner must be a class). */ - final def entered(implicit ctx: Context): this.type = { - assert(this.owner.isClass, s"symbol ($this) entered the scope of non-class owner ${this.owner}") // !!! DEBUG - this.owner.asClass.enter(this) - if (this.is(Module, butNot = Package)) this.owner.asClass.enter(this.moduleClass) - this - } - - /** Enter this symbol in its class owner after given `phase`. Create a fresh - * denotation for its owner class if the class has not yet already one - * that starts being valid after `phase`. - * @pre Symbol is a class member - */ - def enteredAfter(phase: DenotTransformer)(implicit ctx: Context): this.type = - if (ctx.phaseId != phase.next.id) enteredAfter(phase)(ctx.withPhase(phase.next)) - else { - if (this.owner.is(Package)) { - denot.validFor |= InitialPeriod - if (this is Module) this.moduleClass.validFor |= InitialPeriod - } - else this.owner.asClass.ensureFreshScopeAfter(phase) - entered - } - - /** This symbol, if it exists, otherwise the result of evaluating `that` */ - def orElse(that: => Symbol)(implicit ctx: Context) = - if (this.exists) this else that - - /** If this symbol satisfies predicate `p` this symbol, otherwise `NoSymbol` */ - def filter(p: Symbol => Boolean): Symbol = if (p(this)) this else NoSymbol - - /** The current name of this symbol */ - final def name(implicit ctx: Context): ThisName = denot.name.asInstanceOf[ThisName] - - /** The source or class file from which this class or - * the class containing this symbol was generated, null if not applicable. - * Overridden in ClassSymbol - */ - def associatedFile(implicit ctx: Context): AbstractFile = - denot.topLevelClass.symbol.associatedFile - - /** The class file from which this class was generated, null if not applicable. */ - final def binaryFile(implicit ctx: Context): AbstractFile = { - val file = associatedFile - if (file != null && file.path.endsWith("class")) file else null - } - - /** The source file from which this class was generated, null if not applicable. */ - final def sourceFile(implicit ctx: Context): AbstractFile = { - val file = associatedFile - if (file != null && !file.path.endsWith("class")) file - else denot.topLevelClass.getAnnotation(defn.SourceFileAnnot) match { - case Some(sourceAnnot) => sourceAnnot.argumentConstant(0) match { - case Some(Constant(path: String)) => AbstractFile.getFile(path) - case none => null - } - case none => null - } - } - - /** The position of this symbol, or NoPosition is symbol was not loaded - * from source. - */ - def pos: Position = if (coord.isPosition) coord.toPosition else NoPosition - - // TypeParamInfo methods - def isTypeParam(implicit ctx: Context) = denot.is(TypeParam) - def paramName(implicit ctx: Context) = name.asTypeName - def paramBounds(implicit ctx: Context) = denot.info.bounds - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this).bounds - def paramBoundsOrCompleter(implicit ctx: Context): Type = denot.infoOrCompleter - def paramVariance(implicit ctx: Context) = denot.variance - def paramRef(implicit ctx: Context) = denot.typeRef - -// -------- Printing -------------------------------------------------------- - - /** The prefix string to be used when displaying this symbol without denotation */ - protected def prefixString = "Symbol" - - override def toString: String = - if (lastDenot == null) s"Naked$prefixString#$id" - else lastDenot.toString// + "#" + id // !!! DEBUG - - def toText(printer: Printer): Text = printer.toText(this) - - def showLocated(implicit ctx: Context): String = ctx.locatedText(this).show - def showExtendedLocation(implicit ctx: Context): String = ctx.extendedLocationText(this).show - def showDcl(implicit ctx: Context): String = ctx.dclText(this).show - def showKind(implicit ctx: Context): String = ctx.kindString(this) - def showName(implicit ctx: Context): String = ctx.nameString(this) - def showFullName(implicit ctx: Context): String = ctx.fullNameString(this) - - override def hashCode() = id // for debugging. - } - - type TermSymbol = Symbol { type ThisName = TermName } - type TypeSymbol = Symbol { type ThisName = TypeName } - - class ClassSymbol private[Symbols] (coord: Coord, val assocFile: AbstractFile, id: Int) - extends Symbol(coord, id) { - - type ThisName = TypeName - - /** The source or class file from which this class was generated, null if not applicable. */ - override def associatedFile(implicit ctx: Context): AbstractFile = - if (assocFile != null || (this.owner is PackageClass) || this.isEffectiveRoot) assocFile - else super.associatedFile - - final def classDenot(implicit ctx: Context): ClassDenotation = - denot.asInstanceOf[ClassDenotation] - - private var superIdHint: Int = -1 - - override def superId(implicit ctx: Context): Int = { - val hint = superIdHint - if (hint >= 0 && hint <= ctx.lastSuperId && (ctx.classOfId(hint) eq this)) - hint - else { - val id = ctx.superIdOfClass get this match { - case Some(id) => - id - case None => - val id = ctx.nextSuperId - ctx.superIdOfClass(this) = id - ctx.classOfId(id) = this - id - } - superIdHint = id - id - } - } - - override protected def prefixString = "ClassSymbol" - } - - class ErrorSymbol(val underlying: Symbol, msg: => String)(implicit ctx: Context) extends Symbol(NoCoord, ctx.nextId) { - type ThisName = underlying.ThisName - denot = underlying.denot - } - - @sharable object NoSymbol extends Symbol(NoCoord, 0) { - denot = NoDenotation - - override def associatedFile(implicit ctx: Context): AbstractFile = NoSource.file - } - - implicit class Copier[N <: Name](sym: Symbol { type ThisName = N })(implicit ctx: Context) { - /** Copy a symbol, overriding selective fields */ - def copy( - owner: Symbol = sym.owner, - name: N = sym.name, - flags: FlagSet = sym.flags, - info: Type = sym.info, - privateWithin: Symbol = sym.privateWithin, - coord: Coord = sym.coord, - associatedFile: AbstractFile = sym.associatedFile): Symbol = - if (sym.isClass) - ctx.newClassSymbol(owner, name.asTypeName, flags, _ => info, privateWithin, coord, associatedFile) - else - ctx.newSymbol(owner, name, flags, info, privateWithin, coord) - } - - /** Makes all denotation operations available on symbols */ - implicit def toDenot(sym: Symbol)(implicit ctx: Context): SymDenotation = sym.denot - - /** Makes all class denotations available on class symbols */ - implicit def toClassDenot(cls: ClassSymbol)(implicit ctx: Context): ClassDenotation = cls.classDenot - - /** The Definitions object */ - def defn(implicit ctx: Context): Definitions = ctx.definitions - - /** The current class */ - def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass - - @sharable var stubs: List[Symbol] = Nil // diagnostic only -} diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala deleted file mode 100644 index 70819e590..000000000 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ /dev/null @@ -1,688 +0,0 @@ -package dotty.tools.dotc -package core - -import Types._ -import Contexts._ -import Symbols._ -import SymDenotations.{LazyType, TypeParamsCompleter} -import Decorators._ -import util.Stats._ -import util.common._ -import Names._ -import NameOps._ -import Flags._ -import StdNames.tpnme -import util.Positions.Position -import config.Printers.core -import collection.mutable -import dotty.tools.dotc.config.Config -import java.util.NoSuchElementException - -object TypeApplications { - - /** Assert type is not a TypeBounds instance and return it unchanged */ - val noBounds = (tp: Type) => tp match { - case tp: TypeBounds => throw new AssertionError("no TypeBounds allowed") - case _ => tp - } - - /** If `tp` is a TypeBounds instance return its lower bound else return `tp` */ - val boundsToLo = (tp: Type) => tp match { - case tp: TypeBounds => tp.lo - case _ => tp - } - - /** If `tp` is a TypeBounds instance return its upper bound else return `tp` */ - val boundsToHi = (tp: Type) => tp match { - case tp: TypeBounds => tp.hi - case _ => tp - } - - /** Does variance `v1` conform to variance `v2`? - * This is the case if the variances are the same or `sym` is nonvariant. - */ - def varianceConforms(v1: Int, v2: Int): Boolean = - v1 == v2 || v2 == 0 - - /** Does the variance of type parameter `tparam1` conform to the variance of type parameter `tparam2`? - */ - def varianceConforms(tparam1: TypeParamInfo, tparam2: TypeParamInfo)(implicit ctx: Context): Boolean = - varianceConforms(tparam1.paramVariance, tparam2.paramVariance) - - /** Do the variances of type parameters `tparams1` conform to the variances - * of corresponding type parameters `tparams2`? - * This is only the case of `tparams1` and `tparams2` have the same length. - */ - def variancesConform(tparams1: List[TypeParamInfo], tparams2: List[TypeParamInfo])(implicit ctx: Context): Boolean = - tparams1.corresponds(tparams2)(varianceConforms) - - /** Extractor for - * - * [v1 X1: B1, ..., vn Xn: Bn] -> C[X1, ..., Xn] - * - * where v1, ..., vn and B1, ..., Bn are the variances and bounds of the type parameters - * of the class C. - * - * @param tycon C - */ - object EtaExpansion { - def apply(tycon: Type)(implicit ctx: Context) = { - assert(tycon.typeParams.nonEmpty, tycon) - tycon.EtaExpand(tycon.typeParamSymbols) - } - - def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = tp match { - case tp @ PolyType(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn) - case _ => None - } - } - - /** Extractor for type application T[U_1, ..., U_n]. This is the refined type - * - * T { type p_1 v_1= U_1; ...; type p_n v_n= U_n } - * - * where v_i, p_i are the variances and names of the type parameters of T. - */ - object AppliedType { - def apply(tp: Type, args: List[Type])(implicit ctx: Context): Type = tp.appliedTo(args) - - def unapply(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { - case tp: RefinedType => - var refinements: List[RefinedType] = Nil - var tycon = tp.stripTypeVar - while (tycon.isInstanceOf[RefinedType]) { - val rt = tycon.asInstanceOf[RefinedType] - refinements = rt :: refinements - tycon = rt.parent.stripTypeVar - } - def collectArgs(tparams: List[TypeParamInfo], - refinements: List[RefinedType], - argBuf: mutable.ListBuffer[Type]): Option[(Type, List[Type])] = refinements match { - case Nil if tparams.isEmpty && argBuf.nonEmpty => - Some((tycon, argBuf.toList)) - case RefinedType(_, rname, rinfo) :: refinements1 - if tparams.nonEmpty && rname == tparams.head.paramName => - collectArgs(tparams.tail, refinements1, argBuf += rinfo.argInfo) - case _ => - None - } - collectArgs(tycon.typeParams, refinements, new mutable.ListBuffer[Type]) - case HKApply(tycon, args) => - Some((tycon, args)) - case _ => - None - } - } - - /** Adapt all arguments to possible higher-kinded type parameters using etaExpandIfHK - */ - def EtaExpandIfHK(tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): List[Type] = - if (tparams.isEmpty) args - else args.zipWithConserve(tparams)((arg, tparam) => arg.EtaExpandIfHK(tparam.paramBoundsOrCompleter)) - - /** A type map that tries to reduce (part of) the result type of the type lambda `tycon` - * with the given `args`(some of which are wildcard arguments represented by type bounds). - * Non-wildcard arguments are substituted everywhere as usual. A wildcard argument - * `>: L <: H` is substituted for a type lambda parameter `X` only under certain conditions. - * - * 1. If Mode.AllowLambdaWildcardApply is set: - * The wildcard argument is substituted only if `X` appears in a toplevel refinement of the form - * - * { type A = X } - * - * and there are no other occurrences of `X` in the reduced type. In that case - * the refinement above is replaced by - * - * { type A >: L <: U } - * - * The `allReplaced` field indicates whether all occurrences of type lambda parameters - * in the reduced type have been replaced with arguments. - * - * 2. If Mode.AllowLambdaWildcardApply is not set: - * All refinements of the form - * - * { type A = X } - * - * are replaced by: - * - * { type A >: L <: U } - * - * Any other occurrence of `X` in `tycon` is replaced by `U`, if the - * occurrence of `X` in `tycon` is covariant, or nonvariant, or by `L`, - * if the occurrence is contravariant. - * - * The idea is that the `AllowLambdaWildcardApply` mode is used to check whether - * a type can be soundly reduced, and to give an error or warning if that - * is not the case. By contrast, the default mode, with `AllowLambdaWildcardApply` - * not set, reduces all applications even if this yields a different type, so - * its postcondition is that no type parameters of `tycon` appear in the - * result type. Using this mode, we can guarantee that `appliedTo` will never - * produce a higher-kinded application with a type lambda as type constructor. - */ - class Reducer(tycon: PolyType, args: List[Type])(implicit ctx: Context) extends TypeMap { - private var available = (0 until args.length).toSet - var allReplaced = true - def hasWildcardArg(p: PolyParam) = - p.binder == tycon && args(p.paramNum).isInstanceOf[TypeBounds] - def canReduceWildcard(p: PolyParam) = - !ctx.mode.is(Mode.AllowLambdaWildcardApply) || available.contains(p.paramNum) - def apply(t: Type) = t match { - case t @ TypeAlias(p: PolyParam) if hasWildcardArg(p) && canReduceWildcard(p) => - available -= p.paramNum - args(p.paramNum) - case p: PolyParam if p.binder == tycon => - args(p.paramNum) match { - case TypeBounds(lo, hi) => - if (ctx.mode.is(Mode.AllowLambdaWildcardApply)) { allReplaced = false; p } - else if (variance < 0) lo - else hi - case arg => - arg - } - case _: TypeBounds | _: HKApply => - val saved = available - available = Set() - try mapOver(t) - finally available = saved - case _ => - mapOver(t) - } - } -} - -import TypeApplications._ - -/** A decorator that provides methods for modeling type application */ -class TypeApplications(val self: Type) extends AnyVal { - - /** The type parameters of this type are: - * For a ClassInfo type, the type parameters of its class. - * For a typeref referring to a class, the type parameters of the class. - * For a typeref referring to a Lambda class, the type parameters of - * its right hand side or upper bound. - * For a refinement type, the type parameters of its parent, dropping - * any type parameter that is-rebound by the refinement. "Re-bind" means: - * The refinement contains a TypeAlias for the type parameter, or - * it introduces bounds for the type parameter, and we are not in the - * special case of a type Lambda, where a LambdaTrait gets refined - * with the bounds on its hk args. See `LambdaAbstract`, where these - * types get introduced, and see `isBoundedLambda` below for the test. - */ - final def typeParams(implicit ctx: Context): List[TypeParamInfo] = /*>|>*/ track("typeParams") /*<|<*/ { - self match { - case self: ClassInfo => - self.cls.typeParams - case self: PolyType => - self.typeParams - case self: TypeRef => - val tsym = self.symbol - if (tsym.isClass) tsym.typeParams - else if (!tsym.isCompleting) tsym.info.typeParams - else Nil - case self: RefinedType => - self.parent.typeParams.filterNot(_.paramName == self.refinedName) - case self: RecType => - self.parent.typeParams - case _: SingletonType => - Nil - case self: WildcardType => - self.optBounds.typeParams - case self: TypeProxy => - self.superType.typeParams - case _ => - Nil - } - } - - /** If `self` is a higher-kinded type, its type parameters, otherwise Nil */ - final def hkTypeParams(implicit ctx: Context): List[TypeParamInfo] = - if (isHK) typeParams else Nil - - /** If `self` is a generic class, its type parameter symbols, otherwise Nil */ - final def typeParamSymbols(implicit ctx: Context): List[TypeSymbol] = typeParams match { - case (_: Symbol) :: _ => - assert(typeParams.forall(_.isInstanceOf[Symbol])) - typeParams.asInstanceOf[List[TypeSymbol]] - case _ => Nil - } - - /** The named type parameters declared or inherited by this type. - * These are all uninstantiated named type parameters of this type or one - * of its base types. - */ - final def namedTypeParams(implicit ctx: Context): Set[TypeSymbol] = self match { - case self: ClassInfo => - self.cls.namedTypeParams - case self: RefinedType => - self.parent.namedTypeParams.filterNot(_.name == self.refinedName) - case self: SingletonType => - Set() - case self: TypeProxy => - self.underlying.namedTypeParams - case _ => - Set() - } - - /** The smallest supertype of this type that instantiated none of the named type parameters - * in `params`. That is, for each named type parameter `p` in `params`, either there is - * no type field named `p` in this type, or `p` is a named type parameter of this type. - * The first case is important for the recursive case of AndTypes, because some of their operands might - * be missing the named parameter altogether, but the AndType as a whole can still - * contain it. - */ - final def widenToNamedTypeParams(params: Set[TypeSymbol])(implicit ctx: Context): Type = { - - /** Is widening not needed for `tp`? */ - def isOK(tp: Type) = { - val ownParams = tp.namedTypeParams - def isMissingOrOpen(param: TypeSymbol) = { - val ownParam = tp.nonPrivateMember(param.name).symbol - !ownParam.exists || ownParams.contains(ownParam.asType) - } - params.forall(isMissingOrOpen) - } - - /** Widen type by forming the intersection of its widened parents */ - def widenToParents(tp: Type) = { - val parents = tp.parents.map(p => - tp.baseTypeWithArgs(p.symbol).widenToNamedTypeParams(params)) - parents.reduceLeft(ctx.typeComparer.andType(_, _)) - } - - if (isOK(self)) self - else self match { - case self @ AppliedType(tycon, args) if !isOK(tycon) => - widenToParents(self) - case self: TypeRef if self.symbol.isClass => - widenToParents(self) - case self: RefinedType => - val parent1 = self.parent.widenToNamedTypeParams(params) - if (params.exists(_.name == self.refinedName)) parent1 - else self.derivedRefinedType(parent1, self.refinedName, self.refinedInfo) - case self: TypeProxy => - self.superType.widenToNamedTypeParams(params) - case self: AndOrType => - self.derivedAndOrType( - self.tp1.widenToNamedTypeParams(params), self.tp2.widenToNamedTypeParams(params)) - } - } - - /** Is self type higher-kinded (i.e. of kind != "*")? */ - def isHK(implicit ctx: Context): Boolean = self.dealias match { - case self: TypeRef => self.info.isHK - case self: RefinedType => false - case self: PolyType => true - case self: SingletonType => false - case self: TypeVar => - // Using `origin` instead of `underlying`, as is done for typeParams, - // avoids having to set ephemeral in some cases. - self.origin.isHK - case self: WildcardType => self.optBounds.isHK - case self: TypeProxy => self.superType.isHK - case _ => false - } - - /** Dealias type if it can be done without forcing the TypeRef's info */ - def safeDealias(implicit ctx: Context): Type = self match { - case self: TypeRef if self.denot.exists && self.symbol.isAliasType => - self.superType.stripTypeVar.safeDealias - case _ => - self - } - - /** Lambda abstract `self` with given type parameters. Examples: - * - * type T[X] = U becomes type T = [X] -> U - * type T[X] >: L <: U becomes type T >: L <: ([X] -> U) - * - * TODO: Handle parameterized lower bounds - */ - def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = { - def expand(tp: Type) = - PolyType( - tparams.map(_.paramName), tparams.map(_.paramVariance))( - tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds), - tl => tl.lifted(tparams, tp)) - if (tparams.isEmpty) self - else self match { - case self: TypeAlias => - self.derivedTypeAlias(expand(self.alias)) - case self @ TypeBounds(lo, hi) => - self.derivedTypeBounds( - if (lo.isRef(defn.NothingClass)) lo else expand(lo), - expand(hi)) - case _ => expand(self) - } - } - - /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn` - * in a context where type parameters `U1,...,Un` are expected to - * - * LambdaXYZ { Apply = TC[hk$0, ..., hk$n] } - * - * Here, XYZ corresponds to the variances of - * - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`, - * - `T1,...,Tn` otherwise. - * v1 is compatible with v2, if v1 = v2 or v2 is non-variant. - */ - def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { - val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols - self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) - //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") - } - - /** If self is not higher-kinded, eta expand it. */ - def ensureHK(implicit ctx: Context): Type = - if (isHK) self else EtaExpansion(self) - - /** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */ - def EtaExpandIfHK(bound: Type)(implicit ctx: Context): Type = { - val hkParams = bound.hkTypeParams - if (hkParams.isEmpty) self - else self match { - case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length => - EtaExpansion(self) - case _ => self - } - } - - /** If argument A and type parameter P are higher-kinded, adapt the variances - * of A to those of P, ensuring that the variances of the type lambda A - * agree with the variances of corresponding higher-kinded type parameters of P. Example: - * - * class GenericCompanion[+CC[X]] - * GenericCompanion[List] - * - * with adaptHkVariances, the argument `List` will expand to - * - * [X] => List[X] - * - * instead of - * - * [+X] => List[X] - * - * even though `List` is covariant. This adaptation is necessary to ignore conflicting - * variances in overriding members that have types of hk-type parameters such as - * `GenericCompanion[GenTraversable]` or `GenericCompanion[ListBuffer]`. - * When checking overriding, we need to validate the subtype relationship - * - * GenericCompanion[[X] -> ListBuffer[X]] <: GenericCompanion[[+X] -> GenTraversable[X]] - * - * Without adaptation, this would be false, and hence an overriding error would - * result. But with adaptation, the rhs argument will be adapted to - * - * [X] -> GenTraversable[X] - * - * which makes the subtype test succeed. The crucial point here is that, since - * GenericCompanion only expects a non-variant CC, the fact that GenTraversable - * is covariant is irrelevant, so can be ignored. - */ - def adaptHkVariances(bound: Type)(implicit ctx: Context): Type = { - val hkParams = bound.hkTypeParams - if (hkParams.isEmpty) self - else { - def adaptArg(arg: Type): Type = arg match { - case arg @ PolyType(tparams, body) if - !tparams.corresponds(hkParams)(_.paramVariance == _.paramVariance) && - tparams.corresponds(hkParams)(varianceConforms) => - PolyType(tparams.map(_.paramName), hkParams.map(_.paramVariance))( - tl => arg.paramBounds.map(_.subst(arg, tl).bounds), - tl => arg.resultType.subst(arg, tl) - ) - case arg @ TypeAlias(alias) => - arg.derivedTypeAlias(adaptArg(alias)) - case arg @ TypeBounds(lo, hi) => - arg.derivedTypeBounds(adaptArg(lo), adaptArg(hi)) - case _ => - arg - } - adaptArg(self) - } - } - - /** The type representing - * - * T[U1, ..., Un] - * - * where - * @param self = `T` - * @param args = `U1,...,Un` - */ - final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { - val typParams = self.typeParams - def matchParams(t: Type, tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): Type = args match { - case arg :: args1 => - try { - val tparam :: tparams1 = tparams - matchParams(RefinedType(t, tparam.paramName, arg.toBounds(tparam)), tparams1, args1) - } catch { - case ex: MatchError => - println(s"applied type mismatch: $self with underlying ${self.underlyingIfProxy}, args = $args, typeParams = $typParams") // !!! DEBUG - //println(s"precomplete decls = ${self.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}") - throw ex - } - case nil => t - } - val stripped = self.stripTypeVar - val dealiased = stripped.safeDealias - if (args.isEmpty || ctx.erasedTypes) self - else dealiased match { - case dealiased: PolyType => - def tryReduce = - if (!args.exists(_.isInstanceOf[TypeBounds])) { - val followAlias = Config.simplifyApplications && { - dealiased.resType match { - case AppliedType(tyconBody, _) => - variancesConform(typParams, tyconBody.typeParams) - // Reducing is safe for type inference, as kind of type constructor does not change - case _ => false - } - } - if ((dealiased eq stripped) || followAlias) dealiased.instantiate(args) - else HKApply(self, args) - } - else dealiased.resType match { - case AppliedType(tycon, args1) if tycon.safeDealias ne tycon => - // In this case we should always dealias since we cannot handle - // higher-kinded applications to wildcard arguments. - dealiased - .derivedPolyType(resType = tycon.safeDealias.appliedTo(args1)) - .appliedTo(args) - case _ => - val reducer = new Reducer(dealiased, args) - val reduced = reducer(dealiased.resType) - if (reducer.allReplaced) reduced - else HKApply(dealiased, args) - } - tryReduce - case dealiased: AndOrType => - dealiased.derivedAndOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args)) - case dealiased: TypeAlias => - dealiased.derivedTypeAlias(dealiased.alias.appliedTo(args)) - case dealiased: TypeBounds => - dealiased.derivedTypeBounds(dealiased.lo.appliedTo(args), dealiased.hi.appliedTo(args)) - case dealiased: LazyRef => - LazyRef(() => dealiased.ref.appliedTo(args)) - case dealiased: WildcardType => - dealiased - case dealiased: TypeRef if dealiased.symbol == defn.NothingClass => - dealiased - case _ if typParams.isEmpty || typParams.head.isInstanceOf[LambdaParam] => - HKApply(self, args) - case dealiased => - matchParams(dealiased, typParams, args) - } - } - - final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil) - final def appliedTo(arg1: Type, arg2: Type)(implicit ctx: Context): Type = appliedTo(arg1 :: arg2 :: Nil) - - final def applyIfParameterized(args: List[Type])(implicit ctx: Context): Type = - if (typeParams.nonEmpty) appliedTo(args) else self - - /** A cycle-safe version of `appliedTo` where computing type parameters do not force - * the typeconstructor. Instead, if the type constructor is completing, we make - * up hk type parameters matching the arguments. This is needed when unpickling - * Scala2 files such as `scala.collection.generic.Mapfactory`. - */ - final def safeAppliedTo(args: List[Type])(implicit ctx: Context) = self match { - case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting => - HKApply(self, args) - case _ => - appliedTo(args) - } - - /** Turn this type, which is used as an argument for - * type parameter `tparam`, into a TypeBounds RHS - */ - final def toBounds(tparam: TypeParamInfo)(implicit ctx: Context): TypeBounds = self match { - case self: TypeBounds => // this can happen for wildcard args - self - case _ => - val v = tparam.paramVariance - /* Not neeeded. - if (v > 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.upper(self) - else if (v < 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.lower(self) - else - */ - TypeAlias(self, v) - } - - /** The type arguments of this type's base type instance wrt. `base`. - * Existential types in arguments are returned as TypeBounds instances. - */ - final def baseArgInfos(base: Symbol)(implicit ctx: Context): List[Type] = - if (self derivesFrom base) - self.dealias match { - case self: TypeRef if !self.symbol.isClass => self.superType.baseArgInfos(base) - case self: HKApply => self.superType.baseArgInfos(base) - case _ => base.typeParams.map(param => self.member(param.name).info.argInfo) - } - else - Nil - - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are disallowed. - */ - final def baseArgTypes(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve noBounds - - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are approximated by their lower bound. - */ - final def baseArgTypesLo(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve boundsToLo - - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are approximated by their upper bound. - */ - final def baseArgTypesHi(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve boundsToHi - - /** The base type including all type arguments and applicable refinements - * of this type. Refinements are applicable if they refine a member of - * the parent type which furthermore is not a name-mangled type parameter. - * Existential types in arguments are returned as TypeBounds instances. - */ - final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type = ctx.traceIndented(s"btwa ${self.show} wrt $base", core, show = true) { - def default = self.baseTypeRef(base).appliedTo(baseArgInfos(base)) - self match { - case tp: TypeRef => - tp.info match { - case TypeBounds(_, hi) => hi.baseTypeWithArgs(base) - case _ => default - } - case tp @ RefinedType(parent, name, _) if !tp.member(name).symbol.is(ExpandedTypeParam) => - tp.wrapIfMember(parent.baseTypeWithArgs(base)) - case tp: TermRef => - tp.underlying.baseTypeWithArgs(base) - case tp: HKApply => - tp.superType.baseTypeWithArgs(base) - case AndType(tp1, tp2) => - tp1.baseTypeWithArgs(base) & tp2.baseTypeWithArgs(base) - case OrType(tp1, tp2) => - tp1.baseTypeWithArgs(base) | tp2.baseTypeWithArgs(base) - case _ => - default - } - } - - /** Translate a type of the form From[T] to To[T], keep other types as they are. - * `from` and `to` must be static classes, both with one type parameter, and the same variance. - * Do the same for by name types => From[T] and => To[T] - */ - def translateParameterized(from: ClassSymbol, to: ClassSymbol)(implicit ctx: Context): Type = self match { - case self @ ExprType(tp) => - self.derivedExprType(tp.translateParameterized(from, to)) - case _ => - if (self.derivesFrom(from)) - if (ctx.erasedTypes) to.typeRef - else RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info) - else self - } - - /** If this is repeated parameter type, its underlying Seq type, - * or, if isJava is true, Array type, else the type itself. - */ - def underlyingIfRepeated(isJava: Boolean)(implicit ctx: Context): Type = - if (self.isRepeatedParam) { - val seqClass = if (isJava) defn.ArrayClass else defn.SeqClass - translateParameterized(defn.RepeatedParamClass, seqClass) - } - else self - - /** If this is an encoding of a (partially) applied type, return its arguments, - * otherwise return Nil. - * Existential types in arguments are returned as TypeBounds instances. - */ - final def argInfos(implicit ctx: Context): List[Type] = self match { - case AppliedType(tycon, args) => args - case _ => Nil - } - - /** Argument types where existential types in arguments are disallowed */ - def argTypes(implicit ctx: Context) = argInfos mapConserve noBounds - - /** Argument types where existential types in arguments are approximated by their lower bound */ - def argTypesLo(implicit ctx: Context) = argInfos mapConserve boundsToLo - - /** Argument types where existential types in arguments are approximated by their upper bound */ - def argTypesHi(implicit ctx: Context) = argInfos mapConserve boundsToHi - - /** The core type without any type arguments. - * @param `typeArgs` must be the type arguments of this type. - */ - final def withoutArgs(typeArgs: List[Type]): Type = self match { - case HKApply(tycon, args) => tycon - case _ => - typeArgs match { - case _ :: typeArgs1 => - val RefinedType(tycon, _, _) = self - tycon.withoutArgs(typeArgs1) - case nil => - self - } - } - - /** If this is the image of a type argument; recover the type argument, - * otherwise NoType. - */ - final def argInfo(implicit ctx: Context): Type = self match { - case self: TypeAlias => self.alias - case self: TypeBounds => self - case _ => NoType - } - - /** If this is a type alias, its underlying type, otherwise the type itself */ - def dropAlias(implicit ctx: Context): Type = self match { - case TypeAlias(alias) => alias - case _ => self - } - - /** The element type of a sequence or array */ - def elemType(implicit ctx: Context): Type = self match { - case defn.ArrayOf(elemtp) => elemtp - case JavaArrayType(elemtp) => elemtp - case _ => baseArgInfos(defn.SeqClass).headOption.getOrElse(NoType) - } -} diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala deleted file mode 100644 index f78820fff..000000000 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ /dev/null @@ -1,1502 +0,0 @@ -package dotty.tools -package dotc -package core - -import Types._, Contexts._, Symbols._, Flags._, Names._, NameOps._, Denotations._ -import Decorators._ -import StdNames.{nme, tpnme} -import collection.mutable -import util.{Stats, DotClass, SimpleMap} -import config.Config -import config.Printers.{typr, constr, subtyping, noPrinter} -import TypeErasure.{erasedLub, erasedGlb} -import TypeApplications._ -import scala.util.control.NonFatal - -/** Provides methods to compare types. - */ -class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { - implicit val ctx: Context = initctx - - val state = ctx.typerState - import state.constraint - - private var pendingSubTypes: mutable.Set[(Type, Type)] = null - private var recCount = 0 - - private var needsGc = false - - /** Is a subtype check in progress? In that case we may not - * permanently instantiate type variables, because the corresponding - * constraint might still be retracted and the instantiation should - * then be reversed. - */ - def subtypeCheckInProgress: Boolean = { - val result = recCount > 0 - if (result) { - constr.println("*** needsGC ***") - needsGc = true - } - result - } - - /** For statistics: count how many isSubTypes are part of successful comparisons */ - private var successCount = 0 - private var totalCount = 0 - - private var myAnyClass: ClassSymbol = null - private var myNothingClass: ClassSymbol = null - private var myNullClass: ClassSymbol = null - private var myObjectClass: ClassSymbol = null - private var myAnyType: TypeRef = null - private var myNothingType: TypeRef = null - - def AnyClass = { - if (myAnyClass == null) myAnyClass = defn.AnyClass - myAnyClass - } - def NothingClass = { - if (myNothingClass == null) myNothingClass = defn.NothingClass - myNothingClass - } - def NullClass = { - if (myNullClass == null) myNullClass = defn.NullClass - myNullClass - } - def ObjectClass = { - if (myObjectClass == null) myObjectClass = defn.ObjectClass - myObjectClass - } - def AnyType = { - if (myAnyType == null) myAnyType = AnyClass.typeRef - myAnyType - } - def NothingType = { - if (myNothingType == null) myNothingType = NothingClass.typeRef - myNothingType - } - - /** Indicates whether a previous subtype check used GADT bounds */ - var GADTused = false - - /** Record that GADT bounds of `sym` were used in a subtype check. - * But exclude constructor type parameters, as these are aliased - * to the corresponding class parameters, which does not constitute - * a true usage of a GADT symbol. - */ - private def GADTusage(sym: Symbol) = { - if (!sym.owner.isConstructor) GADTused = true - true - } - - // Subtype testing `<:<` - - def topLevelSubType(tp1: Type, tp2: Type): Boolean = { - if (tp2 eq NoType) return false - if ((tp2 eq tp1) || (tp2 eq WildcardType)) return true - try isSubType(tp1, tp2) - finally - if (Config.checkConstraintsSatisfiable) - assert(isSatisfiable, constraint.show) - } - - protected def isSubType(tp1: Type, tp2: Type): Boolean = ctx.traceIndented(s"isSubType ${traceInfo(tp1, tp2)}", subtyping) { - if (tp2 eq NoType) false - else if (tp1 eq tp2) true - else { - val saved = constraint - val savedSuccessCount = successCount - try { - recCount = recCount + 1 - val result = - if (recCount < Config.LogPendingSubTypesThreshold) firstTry(tp1, tp2) - else monitoredIsSubType(tp1, tp2) - recCount = recCount - 1 - if (!result) constraint = saved - else if (recCount == 0 && needsGc) { - state.gc() - needsGc = false - } - if (Stats.monitored) recordStatistics(result, savedSuccessCount) - result - } catch { - case NonFatal(ex) => - if (ex.isInstanceOf[AssertionError]) showGoal(tp1, tp2) - recCount -= 1 - constraint = saved - successCount = savedSuccessCount - throw ex - } - } - } - - private def monitoredIsSubType(tp1: Type, tp2: Type) = { - if (pendingSubTypes == null) { - pendingSubTypes = new mutable.HashSet[(Type, Type)] - ctx.log(s"!!! deep subtype recursion involving ${tp1.show} <:< ${tp2.show}, constraint = ${state.constraint.show}") - ctx.log(s"!!! constraint = ${constraint.show}") - //if (ctx.settings.YnoDeepSubtypes.value) { - // new Error("deep subtype").printStackTrace() - //} - assert(!ctx.settings.YnoDeepSubtypes.value) - if (Config.traceDeepSubTypeRecursions && !this.isInstanceOf[ExplainingTypeComparer]) - ctx.log(TypeComparer.explained(implicit ctx => ctx.typeComparer.isSubType(tp1, tp2))) - } - val p = (tp1, tp2) - !pendingSubTypes(p) && { - try { - pendingSubTypes += p - firstTry(tp1, tp2) - } finally { - pendingSubTypes -= p - } - } - } - - private def firstTry(tp1: Type, tp2: Type): Boolean = tp2 match { - case tp2: NamedType => - def compareNamed(tp1: Type, tp2: NamedType): Boolean = { - implicit val ctx: Context = this.ctx - tp2.info match { - case info2: TypeAlias => isSubType(tp1, info2.alias) - case _ => tp1 match { - case tp1: NamedType => - tp1.info match { - case info1: TypeAlias => - if (isSubType(info1.alias, tp2)) return true - if (tp1.prefix.isStable) return false - // If tp1.prefix is stable, the alias does contain all information about the original ref, so - // there's no need to try something else. (This is important for performance). - // To see why we cannot in general stop here, consider: - // - // trait C { type A } - // trait D { type A = String } - // (C & D)#A <: C#A - // - // Following the alias leads to the judgment `String <: C#A` which is false. - // However the original judgment should be true. - case _ => - } - val sym1 = - if (tp1.symbol.is(ModuleClass) && tp2.symbol.is(ModuleVal)) - // For convenience we want X$ <:< X.type - // This is safe because X$ self-type is X.type - tp1.symbol.companionModule - else - tp1.symbol - if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) - ctx.erasedTypes || - sym1.isStaticOwner || - isSubType(tp1.prefix, tp2.prefix) || - thirdTryNamed(tp1, tp2) - else - ( (tp1.name eq tp2.name) - && isSubType(tp1.prefix, tp2.prefix) - && tp1.signature == tp2.signature - && !tp1.isInstanceOf[WithFixedSym] - && !tp2.isInstanceOf[WithFixedSym] - ) || - thirdTryNamed(tp1, tp2) - case _ => - secondTry(tp1, tp2) - } - } - } - compareNamed(tp1, tp2) - case tp2: ProtoType => - isMatchedByProto(tp2, tp1) - case tp2: BoundType => - tp2 == tp1 || secondTry(tp1, tp2) - case tp2: TypeVar => - isSubType(tp1, tp2.underlying) - case tp2: WildcardType => - def compareWild = tp2.optBounds match { - case TypeBounds(_, hi) => isSubType(tp1, hi) - case NoType => true - } - compareWild - case tp2: LazyRef => - !tp2.evaluating && isSubType(tp1, tp2.ref) - case tp2: AnnotatedType => - isSubType(tp1, tp2.tpe) // todo: refine? - case tp2: ThisType => - def compareThis = { - val cls2 = tp2.cls - tp1 match { - case tp1: ThisType => - // We treat two prefixes A.this, B.this as equivalent if - // A's selftype derives from B and B's selftype derives from A. - val cls1 = tp1.cls - cls1.classInfo.selfType.derivesFrom(cls2) && - cls2.classInfo.selfType.derivesFrom(cls1) - case tp1: NamedType if cls2.is(Module) && cls2.eq(tp1.widen.typeSymbol) => - cls2.isStaticOwner || - isSubType(tp1.prefix, cls2.owner.thisType) || - secondTry(tp1, tp2) - case _ => - secondTry(tp1, tp2) - } - } - compareThis - case tp2: SuperType => - def compareSuper = tp1 match { - case tp1: SuperType => - isSubType(tp1.thistpe, tp2.thistpe) && - isSameType(tp1.supertpe, tp2.supertpe) - case _ => - secondTry(tp1, tp2) - } - compareSuper - case AndType(tp21, tp22) => - isSubType(tp1, tp21) && isSubType(tp1, tp22) - case OrType(tp21, tp22) => - if (tp21.stripTypeVar eq tp22.stripTypeVar) isSubType(tp1, tp21) - else secondTry(tp1, tp2) - case TypeErasure.ErasedValueType(tycon1, underlying2) => - def compareErasedValueType = tp1 match { - case TypeErasure.ErasedValueType(tycon2, underlying1) => - (tycon1.symbol eq tycon2.symbol) && isSameType(underlying1, underlying2) - case _ => - secondTry(tp1, tp2) - } - compareErasedValueType - case ErrorType => - true - case _ => - secondTry(tp1, tp2) - } - - private def secondTry(tp1: Type, tp2: Type): Boolean = tp1 match { - case tp1: NamedType => - tp1.info match { - case info1: TypeAlias => - if (isSubType(info1.alias, tp2)) return true - if (tp1.prefix.isStable) return false - case _ => - } - thirdTry(tp1, tp2) - case tp1: PolyParam => - def flagNothingBound = { - if (!frozenConstraint && tp2.isRef(defn.NothingClass) && state.isGlobalCommittable) { - def msg = s"!!! instantiated to Nothing: $tp1, constraint = ${constraint.show}" - if (Config.failOnInstantiationToNothing) assert(false, msg) - else ctx.log(msg) - } - true - } - def comparePolyParam = - ctx.mode.is(Mode.TypevarsMissContext) || - isSubTypeWhenFrozen(bounds(tp1).hi, tp2) || { - if (canConstrain(tp1)) addConstraint(tp1, tp2, fromBelow = false) && flagNothingBound - else thirdTry(tp1, tp2) - } - comparePolyParam - case tp1: ThisType => - val cls1 = tp1.cls - tp2 match { - case tp2: TermRef if cls1.is(Module) && cls1.eq(tp2.widen.typeSymbol) => - cls1.isStaticOwner || - isSubType(cls1.owner.thisType, tp2.prefix) || - thirdTry(tp1, tp2) - case _ => - thirdTry(tp1, tp2) - } - case tp1: SkolemType => - tp2 match { - case tp2: SkolemType if !ctx.phase.isTyper && tp1.info <:< tp2.info => true - case _ => thirdTry(tp1, tp2) - } - case tp1: TypeVar => - isSubType(tp1.underlying, tp2) - case tp1: WildcardType => - def compareWild = tp1.optBounds match { - case TypeBounds(lo, _) => isSubType(lo, tp2) - case _ => true - } - compareWild - case tp1: LazyRef => - // If `tp1` is in train of being evaluated, don't force it - // because that would cause an assertionError. Return false instead. - // See i859.scala for an example where we hit this case. - !tp1.evaluating && isSubType(tp1.ref, tp2) - case tp1: AnnotatedType => - isSubType(tp1.tpe, tp2) - case AndType(tp11, tp12) => - if (tp11.stripTypeVar eq tp12.stripTypeVar) isSubType(tp11, tp2) - else thirdTry(tp1, tp2) - case tp1 @ OrType(tp11, tp12) => - def joinOK = tp2.dealias match { - case tp12: HKApply => - // If we apply the default algorithm for `A[X] | B[Y] <: C[Z]` where `C` is a - // type parameter, we will instantiate `C` to `A` and then fail when comparing - // with `B[Y]`. To do the right thing, we need to instantiate `C` to the - // common superclass of `A` and `B`. - isSubType(tp1.join, tp2) - case _ => - false - } - joinOK || isSubType(tp11, tp2) && isSubType(tp12, tp2) - case ErrorType => - true - case _ => - thirdTry(tp1, tp2) - } - - private def thirdTryNamed(tp1: Type, tp2: NamedType): Boolean = tp2.info match { - case TypeBounds(lo2, _) => - def compareGADT: Boolean = { - val gbounds2 = ctx.gadt.bounds(tp2.symbol) - (gbounds2 != null) && - (isSubTypeWhenFrozen(tp1, gbounds2.lo) || - narrowGADTBounds(tp2, tp1, isUpper = false)) && - GADTusage(tp2.symbol) - } - ((frozenConstraint || !isCappable(tp1)) && isSubType(tp1, lo2) || - compareGADT || - fourthTry(tp1, tp2)) - - case _ => - val cls2 = tp2.symbol - if (cls2.isClass) { - val base = tp1.baseTypeRef(cls2) - if (base.exists && (base ne tp1)) return isSubType(base, tp2) - if (cls2 == defn.SingletonClass && tp1.isStable) return true - } - fourthTry(tp1, tp2) - } - - private def thirdTry(tp1: Type, tp2: Type): Boolean = tp2 match { - case tp2: NamedType => - thirdTryNamed(tp1, tp2) - case tp2: PolyParam => - def comparePolyParam = - (ctx.mode is Mode.TypevarsMissContext) || - isSubTypeWhenFrozen(tp1, bounds(tp2).lo) || { - if (canConstrain(tp2)) addConstraint(tp2, tp1.widenExpr, fromBelow = true) - else fourthTry(tp1, tp2) - } - comparePolyParam - case tp2: RefinedType => - def compareRefinedSlow: Boolean = { - val name2 = tp2.refinedName - isSubType(tp1, tp2.parent) && - (name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2)) - } - def compareRefined: Boolean = { - val tp1w = tp1.widen - val skipped2 = skipMatching(tp1w, tp2) - if ((skipped2 eq tp2) || !Config.fastPathForRefinedSubtype) - tp1 match { - case tp1: AndType => - // Delay calling `compareRefinedSlow` because looking up a member - // of an `AndType` can lead to a cascade of subtyping checks - // This twist is needed to make collection/generic/ParFactory.scala compile - fourthTry(tp1, tp2) || compareRefinedSlow - case _ => - compareRefinedSlow || fourthTry(tp1, tp2) - } - else // fast path, in particular for refinements resulting from parameterization. - isSubRefinements(tp1w.asInstanceOf[RefinedType], tp2, skipped2) && - isSubType(tp1, skipped2) - } - compareRefined - case tp2: RecType => - def compareRec = tp1.safeDealias match { - case tp1: RecType => - val rthis1 = RecThis(tp1) - isSubType(tp1.parent, tp2.parent.substRecThis(tp2, rthis1)) - case _ => - val tp1stable = ensureStableSingleton(tp1) - isSubType(fixRecs(tp1stable, tp1stable.widenExpr), tp2.parent.substRecThis(tp2, tp1stable)) - } - compareRec - case tp2 @ HKApply(tycon2, args2) => - compareHkApply2(tp1, tp2, tycon2, args2) - case tp2 @ PolyType(tparams2, body2) => - def compareHkLambda: Boolean = tp1.stripTypeVar match { - case tp1 @ PolyType(tparams1, body1) => - /* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail - * The issue is that, logically, bounds should compare contravariantly, - * but that would invalidate a pattern exploited in t2994: - * - * [X0 <: Number] -> Number <:< [X0] -> Any - * - * Under the new scheme, `[X0] -> Any` is NOT a kind that subsumes - * all other bounds. You'd have to write `[X0 >: Any <: Nothing] -> Any` instead. - * This might look weird, but is the only logically correct way to do it. - * - * Note: it would be nice if this could trigger a migration warning, but I - * am not sure how, since the code is buried so deep in subtyping logic. - */ - def boundsOK = - ctx.scala2Mode || - tparams1.corresponds(tparams2)((tparam1, tparam2) => - isSubType(tparam2.paramBounds.subst(tp2, tp1), tparam1.paramBounds)) - val saved = comparedPolyTypes - comparedPolyTypes += tp1 - comparedPolyTypes += tp2 - try - variancesConform(tparams1, tparams2) && - boundsOK && - isSubType(body1, body2.subst(tp2, tp1)) - finally comparedPolyTypes = saved - case _ => - if (!tp1.isHK) { - tp2 match { - case EtaExpansion(tycon2) if tycon2.symbol.isClass => - return isSubType(tp1, tycon2) - case _ => - } - } - fourthTry(tp1, tp2) - } - compareHkLambda - case OrType(tp21, tp22) => - // Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22) - // and analogously for T1 <: T21 | (T221 & T222) - // `|' types to the right of <: are problematic, because - // we have to choose one constraint set or another, which might cut off - // solutions. The rewriting delays the point where we have to choose. - tp21 match { - case AndType(tp211, tp212) => - return isSubType(tp1, OrType(tp211, tp22)) && isSubType(tp1, OrType(tp212, tp22)) - case _ => - } - tp22 match { - case AndType(tp221, tp222) => - return isSubType(tp1, OrType(tp21, tp221)) && isSubType(tp1, OrType(tp21, tp222)) - case _ => - } - either(isSubType(tp1, tp21), isSubType(tp1, tp22)) || fourthTry(tp1, tp2) - case tp2 @ MethodType(_, formals2) => - def compareMethod = tp1 match { - case tp1 @ MethodType(_, formals1) => - (tp1.signature consistentParams tp2.signature) && - matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit && // needed? - isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) - case _ => - false - } - compareMethod - case tp2 @ ExprType(restpe2) => - def compareExpr = tp1 match { - // We allow ()T to be a subtype of => T. - // We need some subtype relationship between them so that e.g. - // def toString and def toString() don't clash when seen - // as members of the same type. And it seems most logical to take - // ()T <:< => T, since everything one can do with a => T one can - // also do with a ()T by automatic () insertion. - case tp1 @ MethodType(Nil, _) => isSubType(tp1.resultType, restpe2) - case _ => isSubType(tp1.widenExpr, restpe2) - } - compareExpr - case tp2 @ TypeBounds(lo2, hi2) => - def compareTypeBounds = tp1 match { - case tp1 @ TypeBounds(lo1, hi1) => - (tp2.variance > 0 && tp1.variance >= 0 || (lo2 eq NothingType) || isSubType(lo2, lo1)) && - (tp2.variance < 0 && tp1.variance <= 0 || (hi2 eq AnyType) || isSubType(hi1, hi2)) - case tp1: ClassInfo => - tp2 contains tp1 - case _ => - false - } - compareTypeBounds - case ClassInfo(pre2, cls2, _, _, _) => - def compareClassInfo = tp1 match { - case ClassInfo(pre1, cls1, _, _, _) => - (cls1 eq cls2) && isSubType(pre1, pre2) - case _ => - false - } - compareClassInfo - case _ => - fourthTry(tp1, tp2) - } - - private def fourthTry(tp1: Type, tp2: Type): Boolean = tp1 match { - case tp1: TypeRef => - tp1.info match { - case TypeBounds(_, hi1) => - def compareGADT = { - val gbounds1 = ctx.gadt.bounds(tp1.symbol) - (gbounds1 != null) && - (isSubTypeWhenFrozen(gbounds1.hi, tp2) || - narrowGADTBounds(tp1, tp2, isUpper = true)) && - GADTusage(tp1.symbol) - } - isSubType(hi1, tp2) || compareGADT - case _ => - def isNullable(tp: Type): Boolean = tp.widenDealias match { - case tp: TypeRef => tp.symbol.isNullableClass - case tp: RefinedOrRecType => isNullable(tp.parent) - case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2) - case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) - case _ => false - } - (tp1.symbol eq NothingClass) && tp2.isValueTypeOrLambda || - (tp1.symbol eq NullClass) && isNullable(tp2) - } - case tp1: SingletonType => - /** if `tp2 == p.type` and `p: q.type` then try `tp1 <:< q.type` as a last effort.*/ - def comparePaths = tp2 match { - case tp2: TermRef => - tp2.info.widenExpr match { - case tp2i: SingletonType => - isSubType(tp1, tp2i) // see z1720.scala for a case where this can arise even in typer. - case _ => false - } - case _ => - false - } - isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths - case tp1: RefinedType => - isNewSubType(tp1.parent, tp2) - case tp1: RecType => - isNewSubType(tp1.parent, tp2) - case tp1 @ HKApply(tycon1, args1) => - compareHkApply1(tp1, tycon1, args1, tp2) - case EtaExpansion(tycon1) => - isSubType(tycon1, tp2) - case AndType(tp11, tp12) => - // Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2 - // and analogously for T11 & (T121 | T122) & T12 <: T2 - // `&' types to the left of <: are problematic, because - // we have to choose one constraint set or another, which might cut off - // solutions. The rewriting delays the point where we have to choose. - tp11 match { - case OrType(tp111, tp112) => - return isSubType(AndType(tp111, tp12), tp2) && isSubType(AndType(tp112, tp12), tp2) - case _ => - } - tp12 match { - case OrType(tp121, tp122) => - return isSubType(AndType(tp11, tp121), tp2) && isSubType(AndType(tp11, tp122), tp2) - case _ => - } - either(isSubType(tp11, tp2), isSubType(tp12, tp2)) - case JavaArrayType(elem1) => - def compareJavaArray = tp2 match { - case JavaArrayType(elem2) => isSubType(elem1, elem2) - case _ => tp2 isRef ObjectClass - } - compareJavaArray - case tp1: ExprType if ctx.phase.id > ctx.gettersPhase.id => - // getters might have converted T to => T, need to compensate. - isSubType(tp1.widenExpr, tp2) - case _ => - false - } - - /** Subtype test for the hk application `tp2 = tycon2[args2]`. - */ - def compareHkApply2(tp1: Type, tp2: HKApply, tycon2: Type, args2: List[Type]): Boolean = { - val tparams = tycon2.typeParams - if (tparams.isEmpty) return false // can happen for ill-typed programs, e.g. neg/tcpoly_overloaded.scala - - /** True if `tp1` and `tp2` have compatible type constructors and their - * corresponding arguments are subtypes relative to their variance (see `isSubArgs`). - */ - def isMatchingApply(tp1: Type): Boolean = tp1 match { - case HKApply(tycon1, args1) => - tycon1.dealias match { - case tycon1: PolyParam => - (tycon1 == tycon2 || - canConstrain(tycon1) && tryInstantiate(tycon1, tycon2)) && - isSubArgs(args1, args2, tparams) - case tycon1: TypeRef => - tycon2.dealias match { - case tycon2: TypeRef if tycon1.symbol == tycon2.symbol => - isSubType(tycon1.prefix, tycon2.prefix) && - isSubArgs(args1, args2, tparams) - case _ => - false - } - case tycon1: TypeVar => - isMatchingApply(tycon1.underlying) - case tycon1: AnnotatedType => - isMatchingApply(tycon1.underlying) - case _ => - false - } - case _ => - false - } - - /** `param2` can be instantiated to a type application prefix of the LHS - * or to a type application prefix of one of the LHS base class instances - * and the resulting type application is a supertype of `tp1`, - * or fallback to fourthTry. - */ - def canInstantiate(tycon2: PolyParam): Boolean = { - - /** Let - * - * `tparams_1, ..., tparams_k-1` be the type parameters of the rhs - * `tparams1_1, ..., tparams1_n-1` be the type parameters of the constructor of the lhs - * `args1_1, ..., args1_n-1` be the type arguments of the lhs - * `d = n - k` - * - * Returns `true` iff `d >= 0` and `tycon2` can be instantiated to - * - * [tparams1_d, ... tparams1_n-1] -> tycon1a[args_1, ..., args_d-1, tparams_d, ... tparams_n-1] - * - * such that the resulting type application is a supertype of `tp1`. - */ - def tyconOK(tycon1a: Type, args1: List[Type]) = { - var tycon1b = tycon1a - val tparams1a = tycon1a.typeParams - val lengthDiff = tparams1a.length - tparams.length - lengthDiff >= 0 && { - val tparams1 = tparams1a.drop(lengthDiff) - variancesConform(tparams1, tparams) && { - if (lengthDiff > 0) - tycon1b = PolyType(tparams1.map(_.paramName), tparams1.map(_.paramVariance))( - tl => tparams1.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds), - tl => tycon1a.appliedTo(args1.take(lengthDiff) ++ - tparams1.indices.toList.map(PolyParam(tl, _)))) - (ctx.mode.is(Mode.TypevarsMissContext) || - tryInstantiate(tycon2, tycon1b.ensureHK)) && - isSubType(tp1, tycon1b.appliedTo(args2)) - } - } - } - - tp1.widen match { - case tp1w @ HKApply(tycon1, args1) => - tyconOK(tycon1, args1) - case tp1w => - tp1w.typeSymbol.isClass && { - val classBounds = tycon2.classSymbols - def liftToBase(bcs: List[ClassSymbol]): Boolean = bcs match { - case bc :: bcs1 => - classBounds.exists(bc.derivesFrom) && - tyconOK(tp1w.baseTypeRef(bc), tp1w.baseArgInfos(bc)) || - liftToBase(bcs1) - case _ => - false - } - liftToBase(tp1w.baseClasses) - } || - fourthTry(tp1, tp2) - } - } - - /** Fall back to comparing either with `fourthTry` or against the lower - * approximation of the rhs. - * @param tyconLo The type constructor's lower approximation. - */ - def fallback(tyconLo: Type) = - either(fourthTry(tp1, tp2), isSubType(tp1, tyconLo.applyIfParameterized(args2))) - - /** Let `tycon2bounds` be the bounds of the RHS type constructor `tycon2`. - * Let `app2 = tp2` where the type constructor of `tp2` is replaced by - * `tycon2bounds.lo`. - * If both bounds are the same, continue with `tp1 <:< app2`. - * otherwise continue with either - * - * tp1 <:< tp2 using fourthTry (this might instantiate params in tp1) - * tp1 <:< app2 using isSubType (this might instantiate params in tp2) - */ - def compareLower(tycon2bounds: TypeBounds, tyconIsTypeRef: Boolean): Boolean = - if (tycon2bounds.lo eq tycon2bounds.hi) - isSubType(tp1, - if (tyconIsTypeRef) tp2.superType - else tycon2bounds.lo.applyIfParameterized(args2)) - else - fallback(tycon2bounds.lo) - - tycon2 match { - case param2: PolyParam => - isMatchingApply(tp1) || { - if (canConstrain(param2)) canInstantiate(param2) - else compareLower(bounds(param2), tyconIsTypeRef = false) - } - case tycon2: TypeRef => - isMatchingApply(tp1) || - compareLower(tycon2.info.bounds, tyconIsTypeRef = true) - case _: TypeVar | _: AnnotatedType => - isSubType(tp1, tp2.superType) - case tycon2: HKApply => - fallback(tycon2.lowerBound) - case _ => - false - } - } - - /** Subtype test for the hk application `tp1 = tycon1[args1]`. - */ - def compareHkApply1(tp1: HKApply, tycon1: Type, args1: List[Type], tp2: Type): Boolean = - tycon1 match { - case param1: PolyParam => - def canInstantiate = tp2 match { - case AppliedType(tycon2, args2) => - tryInstantiate(param1, tycon2.ensureHK) && isSubArgs(args1, args2, tycon2.typeParams) - case _ => - false - } - canConstrain(param1) && canInstantiate || - isSubType(bounds(param1).hi.applyIfParameterized(args1), tp2) - case tycon1: TypeProxy => - isSubType(tp1.superType, tp2) - case _ => - false - } - - /** Subtype test for corresponding arguments in `args1`, `args2` according to - * variances in type parameters `tparams`. - */ - def isSubArgs(args1: List[Type], args2: List[Type], tparams: List[TypeParamInfo]): Boolean = - if (args1.isEmpty) args2.isEmpty - else args2.nonEmpty && { - val v = tparams.head.paramVariance - (v > 0 || isSubType(args2.head, args1.head)) && - (v < 0 || isSubType(args1.head, args2.head)) - } && isSubArgs(args1.tail, args2.tail, tparams) - - /** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where - * - `B` derives from one of the class symbols of `tp2`, - * - the type parameters of `B` match one-by-one the variances of `tparams`, - * - `B` satisfies predicate `p`. - */ - private def testLifted(tp1: Type, tp2: Type, tparams: List[TypeParamInfo], p: Type => Boolean): Boolean = { - val classBounds = tp2.classSymbols - def recur(bcs: List[ClassSymbol]): Boolean = bcs match { - case bc :: bcs1 => - val baseRef = tp1.baseTypeRef(bc) - (classBounds.exists(bc.derivesFrom) && - variancesConform(baseRef.typeParams, tparams) && - p(baseRef.appliedTo(tp1.baseArgInfos(bc))) - || - recur(bcs1)) - case nil => - false - } - recur(tp1.baseClasses) - } - - /** Replace any top-level recursive type `{ z => T }` in `tp` with - * `[z := anchor]T`. - */ - private def fixRecs(anchor: SingletonType, tp: Type): Type = { - def fix(tp: Type): Type = tp.stripTypeVar match { - case tp: RecType => fix(tp.parent).substRecThis(tp, anchor) - case tp @ RefinedType(parent, rname, rinfo) => tp.derivedRefinedType(fix(parent), rname, rinfo) - case tp: PolyParam => fixOrElse(bounds(tp).hi, tp) - case tp: TypeProxy => fixOrElse(tp.underlying, tp) - case tp: AndOrType => tp.derivedAndOrType(fix(tp.tp1), fix(tp.tp2)) - case tp => tp - } - def fixOrElse(tp: Type, fallback: Type) = { - val tp1 = fix(tp) - if (tp1 ne tp) tp1 else fallback - } - fix(tp) - } - - /** Returns true iff the result of evaluating either `op1` or `op2` is true, - * trying at the same time to keep the constraint as wide as possible. - * E.g, if - * - * tp11 <:< tp12 = true with post-constraint c1 - * tp12 <:< tp22 = true with post-constraint c2 - * - * and c1 subsumes c2, then c2 is kept as the post-constraint of the result, - * otherwise c1 is kept. - * - * This method is used to approximate a solution in one of the following cases - * - * T1 & T2 <:< T3 - * T1 <:< T2 | T3 - * - * In the first case (the second one is analogous), we have a choice whether we - * want to establish the subtyping judgement using - * - * T1 <:< T3 or T2 <:< T3 - * - * as a precondition. Either precondition might constrain type variables. - * The purpose of this method is to pick the precondition that constrains less. - * The method is not complete, because sometimes there is no best solution. Example: - * - * A? & B? <: T - * - * Here, each precondition leads to a different constraint, and neither of - * the two post-constraints subsumes the other. - */ - private def either(op1: => Boolean, op2: => Boolean): Boolean = { - val preConstraint = constraint - op1 && { - val leftConstraint = constraint - constraint = preConstraint - if (!(op2 && subsumes(leftConstraint, constraint, preConstraint))) { - if (constr != noPrinter && !subsumes(constraint, leftConstraint, preConstraint)) - constr.println(i"CUT - prefer $leftConstraint over $constraint") - constraint = leftConstraint - } - true - } || op2 - } - - /** Like tp1 <:< tp2, but returns false immediately if we know that - * the case was covered previously during subtyping. - */ - private def isNewSubType(tp1: Type, tp2: Type): Boolean = - if (isCovered(tp1) && isCovered(tp2)) { - //println(s"useless subtype: $tp1 <:< $tp2") - false - } else isSubType(tp1, tp2) - - /** Does type `tp1` have a member with name `name` whose normalized type is a subtype of - * the normalized type of the refinement `tp2`? - * Normalization is as follows: If `tp2` contains a skolem to its refinement type, - * rebase both itself and the member info of `tp` on a freshly created skolem type. - */ - protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = { - val rinfo2 = tp2.refinedInfo - val mbr = tp1.member(name) - - def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2) - - def memberMatches: Boolean = mbr match { // inlined hasAltWith for performance - case mbr: SingleDenotation => qualifies(mbr) - case _ => mbr hasAltWith qualifies - } - - // special case for situations like: - // class C { type T } - // val foo: C - // foo.type <: C { type T {= , <: , >:} foo.T } - def selfReferentialMatch = tp1.isInstanceOf[SingletonType] && { - rinfo2 match { - case rinfo2: TypeBounds => - val mbr1 = tp1.select(name) - !defn.isBottomType(tp1.widen) && - (mbr1 =:= rinfo2.hi || (rinfo2.hi ne rinfo2.lo) && mbr1 =:= rinfo2.lo) - case _ => false - } - } - - /*>|>*/ ctx.traceIndented(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}) ${mbr.info.show} $rinfo2", subtyping) /*<|<*/ { - memberMatches || selfReferentialMatch - } - } - - final def ensureStableSingleton(tp: Type): SingletonType = tp.stripTypeVar match { - case tp: SingletonType if tp.isStable => tp - case tp: ValueType => SkolemType(tp) - case tp: TypeProxy => ensureStableSingleton(tp.underlying) - } - - /** Skip refinements in `tp2` which match corresponding refinements in `tp1`. - * "Match" means: - * - they appear in the same order, - * - they refine the same names, - * - the refinement in `tp1` is an alias type, and - * - neither refinement refers back to the refined type via a refined this. - * @return The parent type of `tp2` after skipping the matching refinements. - */ - private def skipMatching(tp1: Type, tp2: RefinedType): Type = tp1 match { - case tp1 @ RefinedType(parent1, name1, rinfo1: TypeAlias) if name1 == tp2.refinedName => - tp2.parent match { - case parent2: RefinedType => skipMatching(parent1, parent2) - case parent2 => parent2 - } - case _ => tp2 - } - - /** Are refinements in `tp1` pairwise subtypes of the refinements of `tp2` - * up to parent type `limit`? - * @pre `tp1` has the necessary number of refinements, they are type aliases, - * and their names match the corresponding refinements in `tp2`. - * Further, no refinement refers back to the refined type via a refined this. - * The precondition is established by `skipMatching`. - */ - private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean = { - def hasSubRefinement(tp1: RefinedType, refine2: Type): Boolean = { - isSubType(tp1.refinedInfo, refine2) || { - // last effort: try to adapt variances of higher-kinded types if this is sound. - val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info) - adapted2.ne(refine2) && hasSubRefinement(tp1, adapted2) - } - } - hasSubRefinement(tp1, tp2.refinedInfo) && ( - (tp2.parent eq limit) || - isSubRefinements( - tp1.parent.asInstanceOf[RefinedType], tp2.parent.asInstanceOf[RefinedType], limit)) - } - - /** A type has been covered previously in subtype checking if it - * is some combination of TypeRefs that point to classes, where the - * combiners are RefinedTypes, RecTypes, AndTypes or AnnotatedTypes. - * One exception: Refinements referring to basetype args are never considered - * to be already covered. This is necessary because such refined types might - * still need to be compared with a compareAliasRefined. - */ - private def isCovered(tp: Type): Boolean = tp.dealias.stripTypeVar match { - case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass - case tp: ProtoType => false - case tp: RefinedOrRecType => isCovered(tp.parent) - case tp: AnnotatedType => isCovered(tp.underlying) - case AndType(tp1, tp2) => isCovered(tp1) && isCovered(tp2) - case _ => false - } - - /** Defer constraining type variables when compared against prototypes */ - def isMatchedByProto(proto: ProtoType, tp: Type) = tp.stripTypeVar match { - case tp: PolyParam if constraint contains tp => true - case _ => proto.isMatchedBy(tp) - } - - /** Can type `tp` be constrained from above by adding a constraint to - * a typevar that it refers to? In that case we have to be careful not - * to approximate with the lower bound of a type in `thirdTry`. Instead, - * we should first unroll `tp1` until we hit the type variable and bind the - * type variable with (the corresponding type in) `tp2` instead. - */ - private def isCappable(tp: Type): Boolean = tp match { - case tp: PolyParam => constraint contains tp - case tp: TypeProxy => isCappable(tp.underlying) - case tp: AndOrType => isCappable(tp.tp1) || isCappable(tp.tp2) - case _ => false - } - - /** Narrow gadt.bounds for the type parameter referenced by `tr` to include - * `bound` as an upper or lower bound (which depends on `isUpper`). - * Test that the resulting bounds are still satisfiable. - */ - private def narrowGADTBounds(tr: NamedType, bound: Type, isUpper: Boolean): Boolean = - ctx.mode.is(Mode.GADTflexible) && !frozenConstraint && { - val tparam = tr.symbol - typr.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}") - if (bound.isRef(tparam)) false - else bound match { - case bound: TypeRef - if bound.symbol.is(BindDefinedType) && - ctx.gadt.bounds.contains(bound.symbol) && - !tr.symbol.is(BindDefinedType) => - // Avoid having pattern-bound types in gadt bounds, - // as these might be eliminated once the pattern is typechecked. - // Pattern-bound type symbols should be narrowed first, only if that fails - // should symbols in the environment be constrained. - narrowGADTBounds(bound, tr, !isUpper) - case _ => - val oldBounds = ctx.gadt.bounds(tparam) - val newBounds = - if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound) - else TypeBounds(oldBounds.lo | bound, oldBounds.hi) - isSubType(newBounds.lo, newBounds.hi) && - { ctx.gadt.setBounds(tparam, newBounds); true } - } - } - - // Tests around `matches` - - /** A function implementing `tp1` matches `tp2`. */ - final def matchesType(tp1: Type, tp2: Type, relaxed: Boolean): Boolean = tp1.widen match { - case tp1: MethodType => - tp2.widen match { - case tp2: MethodType => - tp1.isImplicit == tp2.isImplicit && - matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) - case tp2 => - relaxed && tp1.paramNames.isEmpty && - matchesType(tp1.resultType, tp2, relaxed) - } - case tp1: PolyType => - tp2.widen match { - case tp2: PolyType => - sameLength(tp1.paramNames, tp2.paramNames) && - matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) - case _ => - false - } - case _ => - tp2.widen match { - case _: PolyType => - false - case tp2: MethodType => - relaxed && tp2.paramNames.isEmpty && - matchesType(tp1, tp2.resultType, relaxed) - case tp2 => - relaxed || isSameType(tp1, tp2) - } - } - - /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ - def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match { - case formal1 :: rest1 => - formals2 match { - case formal2 :: rest2 => - (isSameTypeWhenFrozen(formal1, formal2) - || isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) - || isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && - matchingParams(rest1, rest2, isJava1, isJava2) - case nil => - false - } - case nil => - formals2.isEmpty - } - - /** Do generic types `poly1` and `poly2` have type parameters that - * have the same bounds (after renaming one set to the other)? - */ - def matchingTypeParams(poly1: PolyType, poly2: PolyType): Boolean = - (poly1.paramBounds corresponds poly2.paramBounds)((b1, b2) => - isSameType(b1, b2.subst(poly2, poly1))) - - // Type equality =:= - - /** Two types are the same if are mutual subtypes of each other */ - def isSameType(tp1: Type, tp2: Type): Boolean = - if (tp1 eq NoType) false - else if (tp1 eq tp2) true - else isSubType(tp1, tp2) && isSubType(tp2, tp1) - - /** Same as `isSameType` but also can be applied to overloaded TermRefs, where - * two overloaded refs are the same if they have pairwise equal alternatives - */ - def isSameRef(tp1: Type, tp2: Type): Boolean = ctx.traceIndented(s"isSameRef($tp1, $tp2") { - def isSubRef(tp1: Type, tp2: Type): Boolean = tp1 match { - case tp1: TermRef if tp1.isOverloaded => - tp1.alternatives forall (isSubRef(_, tp2)) - case _ => - tp2 match { - case tp2: TermRef if tp2.isOverloaded => - tp2.alternatives exists (isSubRef(tp1, _)) - case _ => - isSubType(tp1, tp2) - } - } - isSubRef(tp1, tp2) && isSubRef(tp2, tp1) - } - - /** The greatest lower bound of two types */ - def glb(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ { - if (tp1 eq tp2) tp1 - else if (!tp1.exists) tp2 - else if (!tp2.exists) tp1 - else if ((tp1 isRef AnyClass) || (tp2 isRef NothingClass)) tp2 - else if ((tp2 isRef AnyClass) || (tp1 isRef NothingClass)) tp1 - else tp2 match { // normalize to disjunctive normal form if possible. - case OrType(tp21, tp22) => - tp1 & tp21 | tp1 & tp22 - case _ => - tp1 match { - case OrType(tp11, tp12) => - tp11 & tp2 | tp12 & tp2 - case _ => - val t1 = mergeIfSub(tp1, tp2) - if (t1.exists) t1 - else { - val t2 = mergeIfSub(tp2, tp1) - if (t2.exists) t2 - else tp1 match { - case tp1: ConstantType => - tp2 match { - case tp2: ConstantType => - // Make use of the fact that the intersection of two constant types - // types which are not subtypes of each other is known to be empty. - // Note: The same does not apply to singleton types in general. - // E.g. we could have a pattern match against `x.type & y.type` - // which might succeed if `x` and `y` happen to be the same ref - // at run time. It would not work to replace that with `Nothing`. - // However, maybe we can still apply the replacement to - // types which are not explicitly written. - defn.NothingType - case _ => andType(tp1, tp2) - } - case _ => andType(tp1, tp2) - } - } - } - } - } - - /** The greatest lower bound of a list types */ - final def glb(tps: List[Type]): Type = - ((defn.AnyType: Type) /: tps)(glb) - - /** The least upper bound of two types - * @note We do not admit singleton types in or-types as lubs. - */ - def lub(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ { - if (tp1 eq tp2) tp1 - else if (!tp1.exists) tp1 - else if (!tp2.exists) tp2 - else if ((tp1 isRef AnyClass) || (tp2 isRef NothingClass)) tp1 - else if ((tp2 isRef AnyClass) || (tp1 isRef NothingClass)) tp2 - else { - val t1 = mergeIfSuper(tp1, tp2) - if (t1.exists) t1 - else { - val t2 = mergeIfSuper(tp2, tp1) - if (t2.exists) t2 - else { - val tp1w = tp1.widen - val tp2w = tp2.widen - if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w) - else orType(tp1w, tp2w) // no need to check subtypes again - } - } - } - } - - /** The least upper bound of a list of types */ - final def lub(tps: List[Type]): Type = - ((defn.NothingType: Type) /: tps)(lub) - - /** Merge `t1` into `tp2` if t1 is a subtype of some &-summand of tp2. - */ - private def mergeIfSub(tp1: Type, tp2: Type): Type = - if (isSubTypeWhenFrozen(tp1, tp2)) - if (isSubTypeWhenFrozen(tp2, tp1)) tp2 else tp1 // keep existing type if possible - else tp2 match { - case tp2 @ AndType(tp21, tp22) => - val lower1 = mergeIfSub(tp1, tp21) - if (lower1 eq tp21) tp2 - else if (lower1.exists) lower1 & tp22 - else { - val lower2 = mergeIfSub(tp1, tp22) - if (lower2 eq tp22) tp2 - else if (lower2.exists) tp21 & lower2 - else NoType - } - case _ => - NoType - } - - /** Merge `tp1` into `tp2` if tp1 is a supertype of some |-summand of tp2. - */ - private def mergeIfSuper(tp1: Type, tp2: Type): Type = - if (isSubTypeWhenFrozen(tp2, tp1)) - if (isSubTypeWhenFrozen(tp1, tp2)) tp2 else tp1 // keep existing type if possible - else tp2 match { - case tp2 @ OrType(tp21, tp22) => - val higher1 = mergeIfSuper(tp1, tp21) - if (higher1 eq tp21) tp2 - else if (higher1.exists) higher1 | tp22 - else { - val higher2 = mergeIfSuper(tp1, tp22) - if (higher2 eq tp22) tp2 - else if (higher2.exists) tp21 | higher2 - else NoType - } - case _ => - NoType - } - - /** Form a normalized conjunction of two types. - * Note: For certain types, `&` is distributed inside the type. This holds for - * all types which are not value types (e.g. TypeBounds, ClassInfo, - * ExprType, MethodType, PolyType). Also, when forming an `&`, - * instantiated TypeVars are dereferenced and annotations are stripped. - * Finally, refined types with the same refined name are - * opportunistically merged. - * - * Sometimes, the conjunction of two types cannot be formed because - * the types are in conflict of each other. In particular: - * - * 1. Two different class types are conflicting. - * 2. A class type conflicts with a type bounds that does not include the class reference. - * 3. Two method or poly types with different (type) parameters but the same - * signature are conflicting - * - * In these cases, a MergeError is thrown. - */ - final def andType(tp1: Type, tp2: Type, erased: Boolean = ctx.erasedTypes) = ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) { - val t1 = distributeAnd(tp1, tp2) - if (t1.exists) t1 - else { - val t2 = distributeAnd(tp2, tp1) - if (t2.exists) t2 - else if (erased) erasedGlb(tp1, tp2, isJava = false) - else liftIfHK(tp1, tp2, AndType(_, _), _ & _) - } - } - - /** Form a normalized conjunction of two types. - * Note: For certain types, `|` is distributed inside the type. This holds for - * all types which are not value types (e.g. TypeBounds, ClassInfo, - * ExprType, MethodType, PolyType). Also, when forming an `|`, - * instantiated TypeVars are dereferenced and annotations are stripped. - * - * Sometimes, the disjunction of two types cannot be formed because - * the types are in conflict of each other. (@see `andType` for an enumeration - * of these cases). In cases of conflict a `MergeError` is raised. - * - * @param erased Apply erasure semantics. If erased is true, instead of creating - * an OrType, the lub will be computed using TypeCreator#erasedLub. - */ - final def orType(tp1: Type, tp2: Type, erased: Boolean = ctx.erasedTypes) = { - val t1 = distributeOr(tp1, tp2) - if (t1.exists) t1 - else { - val t2 = distributeOr(tp2, tp1) - if (t2.exists) t2 - else if (erased) erasedLub(tp1, tp2) - else liftIfHK(tp1, tp2, OrType(_, _), _ | _) - } - } - - /** `op(tp1, tp2)` unless `tp1` and `tp2` are type-constructors with at least - * some unnamed type parameters. - * In the latter case, combine `tp1` and `tp2` under a type lambda like this: - * - * [X1, ..., Xn] -> op(tp1[X1, ..., Xn], tp2[X1, ..., Xn]) - * - * Note: There is a tension between named and positional parameters here, which - * is impossible to resolve completely. Say you have - * - * C[type T], D[type U] - * - * Then do you expand `C & D` to `[T] -> C[T] & D[T]` or not? Under the named - * type parameter interpretation, this would be wrong whereas under the traditional - * higher-kinded interpretation this would be required. The problem arises from - * allowing both interpretations. A possible remedy is to be somehow stricter - * in where we allow which interpretation. - */ - private def liftIfHK(tp1: Type, tp2: Type, op: (Type, Type) => Type, original: (Type, Type) => Type) = { - val tparams1 = tp1.typeParams - val tparams2 = tp2.typeParams - if (tparams1.isEmpty) - if (tparams2.isEmpty) op(tp1, tp2) - else original(tp1, tp2.appliedTo(tp2.typeParams.map(_.paramBoundsAsSeenFrom(tp2)))) - else if (tparams2.isEmpty) - original(tp1.appliedTo(tp1.typeParams.map(_.paramBoundsAsSeenFrom(tp1))), tp2) - else - PolyType( - paramNames = tpnme.syntheticTypeParamNames(tparams1.length), - variances = (tparams1, tparams2).zipped.map((tparam1, tparam2) => - (tparam1.paramVariance + tparam2.paramVariance) / 2))( - paramBoundsExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) => - tl.lifted(tparams1, tparam1.paramBoundsAsSeenFrom(tp1)).bounds & - tl.lifted(tparams2, tparam2.paramBoundsAsSeenFrom(tp2)).bounds), - resultTypeExp = tl => - original(tl.lifted(tparams1, tp1).appliedTo(tl.paramRefs), - tl.lifted(tparams2, tp2).appliedTo(tl.paramRefs))) - } - - /** Try to distribute `&` inside type, detect and handle conflicts - * @pre !(tp1 <: tp2) && !(tp2 <:< tp1) -- these cases were handled before - */ - private def distributeAnd(tp1: Type, tp2: Type): Type = tp1 match { - // opportunistically merge same-named refinements - // this does not change anything semantically (i.e. merging or not merging - // gives =:= types), but it keeps the type smaller. - case tp1: RefinedType => - tp2 match { - case tp2: RefinedType if tp1.refinedName == tp2.refinedName => - // Given two refinements `T1 { X = S1 }` and `T2 { X = S2 }`, if `S1 =:= S2` - // (possibly by instantiating type parameters), rewrite to `T1 & T2 { X = S1 }`. - // Otherwise rewrite to `T1 & T2 { X B }` where `B` is the conjunction of - // the bounds of `X` in `T1` and `T2`. - // The first rule above is contentious because it cuts the constraint set. - // But without it we would replace the two aliases by - // `T { X >: S1 | S2 <: S1 & S2 }`, which looks weird and is probably - // not what's intended. - val rinfo1 = tp1.refinedInfo - val rinfo2 = tp2.refinedInfo - val parent = tp1.parent & tp2.parent - val rinfo = - if (rinfo1.isAlias && rinfo2.isAlias && isSameType(rinfo1, rinfo2)) - rinfo1 - else - rinfo1 & rinfo2 - tp1.derivedRefinedType(parent, tp1.refinedName, rinfo) - case _ => - NoType - } - case tp1: RecType => - tp1.rebind(distributeAnd(tp1.parent, tp2)) - case ExprType(rt1) => - tp2 match { - case ExprType(rt2) => - ExprType(rt1 & rt2) - case _ => - rt1 & tp2 - } - case tp1: TypeVar if tp1.isInstantiated => - tp1.underlying & tp2 - case tp1: AnnotatedType => - tp1.underlying & tp2 - case _ => - NoType - } - - /** Try to distribute `|` inside type, detect and handle conflicts - * Note that, unlike for `&`, a disjunction cannot be pushed into - * a refined or applied type. Example: - * - * List[T] | List[U] is not the same as List[T | U]. - * - * The rhs is a proper supertype of the lhs. - */ - private def distributeOr(tp1: Type, tp2: Type): Type = tp1 match { - case ExprType(rt1) => - ExprType(rt1 | tp2.widenExpr) - case tp1: TypeVar if tp1.isInstantiated => - tp1.underlying | tp2 - case tp1: AnnotatedType => - tp1.underlying | tp2 - case _ => - NoType - } - - /** Show type, handling type types better than the default */ - private def showType(tp: Type)(implicit ctx: Context) = tp match { - case ClassInfo(_, cls, _, _, _) => cls.showLocated - case bounds: TypeBounds => "type bounds" + bounds.show - case _ => tp.show - } - - /** A comparison function to pick a winner in case of a merge conflict */ - private def isAsGood(tp1: Type, tp2: Type): Boolean = tp1 match { - case tp1: ClassInfo => - tp2 match { - case tp2: ClassInfo => - isSubTypeWhenFrozen(tp1.prefix, tp2.prefix) || (tp1.cls.owner derivesFrom tp2.cls.owner) - case _ => - false - } - case tp1: PolyType => - tp2 match { - case tp2: PolyType => - tp1.typeParams.length == tp2.typeParams.length && - isAsGood(tp1.resultType, tp2.resultType.subst(tp2, tp1)) - case _ => - false - } - case tp1: MethodType => - tp2 match { - case tp2: MethodType => - def asGoodParams(formals1: List[Type], formals2: List[Type]) = - (formals2 corresponds formals1)(isSubTypeWhenFrozen) - asGoodParams(tp1.paramTypes, tp2.paramTypes) && - (!asGoodParams(tp2.paramTypes, tp1.paramTypes) || - isAsGood(tp1.resultType, tp2.resultType)) - case _ => - false - } - case _ => - false - } - - /** A new type comparer of the same type as this one, using the given context. */ - def copyIn(ctx: Context) = new TypeComparer(ctx) - - // ----------- Diagnostics -------------------------------------------------- - - /** A hook for showing subtype traces. Overridden in ExplainingTypeComparer */ - def traceIndented[T](str: String)(op: => T): T = op - - private def traceInfo(tp1: Type, tp2: Type) = - s"${tp1.show} <:< ${tp2.show}" + { - if (ctx.settings.verbose.value || Config.verboseExplainSubtype) { - s" ${tp1.getClass}, ${tp2.getClass}" + - (if (frozenConstraint) " frozen" else "") + - (if (ctx.mode is Mode.TypevarsMissContext) " tvars-miss-ctx" else "") - } - else "" - } - - /** Show subtype goal that led to an assertion failure */ - def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = { - println(ex"assertion failure for $tp1 <:< $tp2, frozen = $frozenConstraint") - def explainPoly(tp: Type) = tp match { - case tp: PolyParam => ctx.echo(s"polyparam ${tp.show} found in ${tp.binder.show}") - case tp: TypeRef if tp.symbol.exists => ctx.echo(s"typeref ${tp.show} found in ${tp.symbol.owner.show}") - case tp: TypeVar => ctx.echo(s"typevar ${tp.show}, origin = ${tp.origin}") - case _ => ctx.echo(s"${tp.show} is a ${tp.getClass}") - } - explainPoly(tp1) - explainPoly(tp2) - } - - /** Record statistics about the total number of subtype checks - * and the number of "successful" subtype checks, i.e. checks - * that form part of a subtype derivation tree that's ultimately successful. - */ - def recordStatistics(result: Boolean, prevSuccessCount: Int) = { - // Stats.record(s"isSubType ${tp1.show} <:< ${tp2.show}") - totalCount += 1 - if (result) successCount += 1 else successCount = prevSuccessCount - if (recCount == 0) { - Stats.record("successful subType", successCount) - Stats.record("total subType", totalCount) - successCount = 0 - totalCount = 0 - } - } -} - -object TypeComparer { - - /** Show trace of comparison operations when performing `op` as result string */ - def explained[T](op: Context => T)(implicit ctx: Context): String = { - val nestedCtx = ctx.fresh.setTypeComparerFn(new ExplainingTypeComparer(_)) - op(nestedCtx) - nestedCtx.typeComparer.toString - } -} - -/** A type comparer that can record traces of subtype operations */ -class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { - private var indent = 0 - private val b = new StringBuilder - - private var skipped = false - - override def traceIndented[T](str: String)(op: => T): T = - if (skipped) op - else { - indent += 2 - b append "\n" append (" " * indent) append "==> " append str - val res = op - b append "\n" append (" " * indent) append "<== " append str append " = " append show(res) - indent -= 2 - res - } - - private def show(res: Any) = res match { - case res: printing.Showable if !ctx.settings.Yexplainlowlevel.value => res.show - case _ => String.valueOf(res) - } - - override def isSubType(tp1: Type, tp2: Type) = - traceIndented(s"${show(tp1)} <:< ${show(tp2)}${if (Config.verboseExplainSubtype) s" ${tp1.getClass} ${tp2.getClass}" else ""}${if (frozenConstraint) " frozen" else ""}") { - super.isSubType(tp1, tp2) - } - - override def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = - traceIndented(s"hasMatchingMember(${show(tp1)} . $name, ${show(tp2.refinedInfo)}), member = ${show(tp1.member(name).info)}") { - super.hasMatchingMember(name, tp1, tp2) - } - - override def lub(tp1: Type, tp2: Type) = - traceIndented(s"lub(${show(tp1)}, ${show(tp2)})") { - super.lub(tp1, tp2) - } - - override def glb(tp1: Type, tp2: Type) = - traceIndented(s"glb(${show(tp1)}, ${show(tp2)})") { - super.glb(tp1, tp2) - } - - override def addConstraint(param: PolyParam, bound: Type, fromBelow: Boolean): Boolean = - traceIndented(i"add constraint $param ${if (fromBelow) ">:" else "<:"} $bound $frozenConstraint, constraint = ${ctx.typerState.constraint}") { - super.addConstraint(param, bound, fromBelow) - } - - override def copyIn(ctx: Context) = new ExplainingTypeComparer(ctx) - - override def compareHkApply2(tp1: Type, tp2: HKApply, tycon2: Type, args2: List[Type]): Boolean = { - def addendum = "" - traceIndented(i"compareHkApply $tp1, $tp2$addendum") { - super.compareHkApply2(tp1, tp2, tycon2, args2) - } - } - - override def toString = "Subtype trace:" + { try b.toString finally b.clear() } -} diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala deleted file mode 100644 index abbacee49..000000000 --- a/src/dotty/tools/dotc/core/TypeErasure.scala +++ /dev/null @@ -1,514 +0,0 @@ -package dotty.tools -package dotc -package core - -import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined -import Uniques.unique -import dotc.transform.ExplicitOuter._ -import dotc.transform.ValueClasses._ -import util.DotClass - -/** Erased types are: - * - * ErasedValueType - * TypeRef(prefix is ignored, denot is ClassDenotation) - * TermRef(prefix is ignored, denot is SymDenotation) - * JavaArrayType - * AnnotatedType - * MethodType - * ThisType - * SuperType - * ClassInfo (NoPrefix, ...) - * NoType - * NoPrefix - * WildcardType - * ErrorType - * - * only for isInstanceOf, asInstanceOf: PolyType, PolyParam, TypeBounds - * - */ -object TypeErasure { - - /** A predicate that tests whether a type is a legal erased type. Only asInstanceOf and - * isInstanceOf may have types that do not satisfy the predicate. - * ErasedValueType is considered an erased type because it is valid after Erasure (it is - * eliminated by ElimErasedValueType). - */ - def isErasedType(tp: Type)(implicit ctx: Context): Boolean = tp match { - case _: ErasedValueType => - true - case tp: TypeRef => - tp.symbol.isClass && tp.symbol != defn.AnyClass && tp.symbol != defn.ArrayClass - case _: TermRef => - true - case JavaArrayType(elem) => - isErasedType(elem) - case AnnotatedType(tp, _) => - isErasedType(tp) - case ThisType(tref) => - isErasedType(tref) - case tp: MethodType => - tp.paramTypes.forall(isErasedType) && isErasedType(tp.resultType) - case tp @ ClassInfo(pre, _, parents, decls, _) => - isErasedType(pre) && parents.forall(isErasedType) //&& decls.forall(sym => isErasedType(sym.info)) && isErasedType(tp.selfType) - case NoType | NoPrefix | WildcardType | ErrorType | SuperType(_, _) => - true - case _ => - false - } - - /** A type representing the semi-erasure of a derived value class, see SIP-15 - * where it's called "C$unboxed" for a class C. - * Derived value classes are erased to this type during Erasure (when - * semiEraseVCs = true) and subsequently erased to their underlying type - * during ElimErasedValueType. This type is outside the normal Scala class - * hierarchy: it is a subtype of no other type and is a supertype only of - * Nothing. This is because this type is only useful for type adaptation (see - * [[Erasure.Boxing#adaptToType]]). - * - * @param tycon A TypeRef referring to the value class symbol - * @param erasedUnderlying The erased type of the single field of the value class - */ - abstract case class ErasedValueType(tycon: TypeRef, erasedUnderlying: Type) - extends CachedGroundType with ValueType { - override def computeHash = doHash(tycon, erasedUnderlying) - } - - final class CachedErasedValueType(tycon: TypeRef, erasedUnderlying: Type) - extends ErasedValueType(tycon, erasedUnderlying) - - object ErasedValueType { - def apply(tycon: TypeRef, erasedUnderlying: Type)(implicit ctx: Context) = { - unique(new CachedErasedValueType(tycon, erasedUnderlying)) - } - } - - private def erasureIdx(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean, wildcardOK: Boolean) = - (if (isJava) 1 else 0) + - (if (semiEraseVCs) 2 else 0) + - (if (isConstructor) 4 else 0) + - (if (wildcardOK) 8 else 0) - - private val erasures = new Array[TypeErasure](16) - - for { - isJava <- List(false, true) - semiEraseVCs <- List(false, true) - isConstructor <- List(false, true) - wildcardOK <- List(false, true) - } erasures(erasureIdx(isJava, semiEraseVCs, isConstructor, wildcardOK)) = - new TypeErasure(isJava, semiEraseVCs, isConstructor, wildcardOK) - - /** Produces an erasure function. See the documentation of the class [[TypeErasure]] - * for a description of each parameter. - */ - private def erasureFn(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean, wildcardOK: Boolean): TypeErasure = - erasures(erasureIdx(isJava, semiEraseVCs, isConstructor, wildcardOK)) - - /** The current context with a phase no later than erasure */ - private def erasureCtx(implicit ctx: Context) = - if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase) else ctx - - /** The standard erasure of a Scala type. Value classes are erased as normal classes. - * - * @param tp The type to erase. - */ - def erasure(tp: Type)(implicit ctx: Context): Type = - erasureFn(isJava = false, semiEraseVCs = false, isConstructor = false, wildcardOK = false)(tp)(erasureCtx) - - /** The value class erasure of a Scala type, where value classes are semi-erased to - * ErasedValueType (they will be fully erased in [[ElimErasedValueType]]). - * - * @param tp The type to erase. - */ - def valueErasure(tp: Type)(implicit ctx: Context): Type = - erasureFn(isJava = false, semiEraseVCs = true, isConstructor = false, wildcardOK = false)(tp)(erasureCtx) - - def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = { - val normTp = - if (tp.isRepeatedParam) { - val seqClass = if (isJava) defn.ArrayClass else defn.SeqClass - tp.translateParameterized(defn.RepeatedParamClass, seqClass) - } - else tp - val erase = erasureFn(isJava, semiEraseVCs = false, isConstructor = false, wildcardOK = true) - erase.sigName(normTp)(erasureCtx) - } - - /** The erasure of a top-level reference. Differs from normal erasure in that - * TermRefs are kept instead of being widened away. - */ - def erasedRef(tp: Type)(implicit ctx: Context): Type = tp match { - case tp: TermRef => - assert(tp.symbol.exists, tp) - val tp1 = ctx.makePackageObjPrefixExplicit(tp) - if (tp1 ne tp) erasedRef(tp1) - else TermRef(erasedRef(tp.prefix), tp.symbol.asTerm) - case tp: ThisType => - tp - case tp => - valueErasure(tp) - } - - /** The symbol's erased info. This is the type's erasure, except for the following symbols: - * - * - For $asInstanceOf : [T]T - * - For $isInstanceOf : [T]Boolean - * - For all abstract types : = ? - * - For companion methods : the erasure of their type with semiEraseVCs = false. - * The signature of these methods are used to keep a - * link between companions and should not be semi-erased. - * - For Java-defined symbols: : the erasure of their type with isJava = true, - * semiEraseVCs = false. Semi-erasure never happens in Java. - * - For all other symbols : the semi-erasure of their types, with - * isJava, isConstructor set according to symbol. - */ - def transformInfo(sym: Symbol, tp: Type)(implicit ctx: Context): Type = { - val isJava = sym is JavaDefined - val semiEraseVCs = !isJava && !sym.isCompanionMethod - val erase = erasureFn(isJava, semiEraseVCs, sym.isConstructor, wildcardOK = false) - - def eraseParamBounds(tp: PolyType): Type = - tp.derivedPolyType( - tp.paramNames, tp.paramNames map (Function.const(TypeBounds.upper(defn.ObjectType))), tp.resultType) - - if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) - else if (sym.isAbstractType) TypeAlias(WildcardType) - else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) - else erase.eraseInfo(tp, sym)(erasureCtx) match { - case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) => - MethodType(Nil, defn.BoxedUnitType) - case einfo => - einfo - } - } - - /** Is `tp` an abstract type or polymorphic type parameter that has `Any`, `AnyVal`, - * or a universal trait as upper bound and that is not Java defined? Arrays of such types are - * erased to `Object` instead of `Object[]`. - */ - def isUnboundedGeneric(tp: Type)(implicit ctx: Context): Boolean = tp.dealias match { - case tp: TypeRef => - !tp.symbol.isClass && - !tp.derivesFrom(defn.ObjectClass) && - !tp.symbol.is(JavaDefined) - case tp: PolyParam => - !tp.derivesFrom(defn.ObjectClass) && - !tp.binder.resultType.isInstanceOf[JavaMethodType] - case tp: TypeAlias => isUnboundedGeneric(tp.alias) - case tp: TypeBounds => !tp.hi.derivesFrom(defn.ObjectClass) - case tp: TypeProxy => isUnboundedGeneric(tp.underlying) - case tp: AndType => isUnboundedGeneric(tp.tp1) || isUnboundedGeneric(tp.tp2) - case tp: OrType => isUnboundedGeneric(tp.tp1) && isUnboundedGeneric(tp.tp2) - case _ => false - } - - /** The erased least upper bound is computed as follows - * - if both argument are arrays of objects, an array of the lub of the element types - * - if both arguments are arrays of same primitives, an array of this primitive - * - if one argument is array of primitives and the other is array of objects, Object - * - if one argument is an array, Object - * - otherwise a common superclass or trait S of the argument classes, with the - * following two properties: - * S is minimal: no other common superclass or trait derives from S] - * S is last : in the linearization of the first argument type `tp1` - * there are no minimal common superclasses or traits that - * come after S. - * (the reason to pick last is that we prefer classes over traits that way). - */ - def erasedLub(tp1: Type, tp2: Type)(implicit ctx: Context): Type = tp1 match { - case JavaArrayType(elem1) => - import dotty.tools.dotc.transform.TypeUtils._ - tp2 match { - case JavaArrayType(elem2) => - if (elem1.isPrimitiveValueType || elem2.isPrimitiveValueType) { - if (elem1.classSymbol eq elem2.classSymbol) // same primitive - JavaArrayType(elem1) - else defn.ObjectType - } else JavaArrayType(erasedLub(elem1, elem2)) - case _ => defn.ObjectType - } - case _ => - tp2 match { - case JavaArrayType(_) => defn.ObjectType - case _ => - val cls2 = tp2.classSymbol - def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match { - case bc :: bcs1 => - if (cls2.derivesFrom(bc)) - if (!bc.is(Trait) && bc != defn.AnyClass) bc - else loop(bcs1, if (bestSoFar.derivesFrom(bc)) bestSoFar else bc) - else - loop(bcs1, bestSoFar) - case nil => - bestSoFar - } - val t = loop(tp1.baseClasses, defn.ObjectClass) - if (t eq defn.AnyValClass) - // while AnyVal is a valid common super class for primitives it does not exist after erasure - defn.ObjectType - else t.typeRef - } - } - - /** The erased greatest lower bound picks one of the two argument types. It prefers, in this order: - * - arrays over non-arrays - * - subtypes over supertypes, unless isJava is set - * - real classes over traits - */ - def erasedGlb(tp1: Type, tp2: Type, isJava: Boolean)(implicit ctx: Context): Type = tp1 match { - case JavaArrayType(elem1) => - tp2 match { - case JavaArrayType(elem2) => JavaArrayType(erasedGlb(elem1, elem2, isJava)) - case _ => tp1 - } - case _ => - tp2 match { - case JavaArrayType(_) => tp2 - case _ => - val tsym1 = tp1.typeSymbol - val tsym2 = tp2.typeSymbol - if (!tsym2.exists) tp1 - else if (!tsym1.exists) tp2 - else if (!isJava && tsym1.derivesFrom(tsym2)) tp1 - else if (!isJava && tsym2.derivesFrom(tsym1)) tp2 - else if (tp1.typeSymbol.isRealClass) tp1 - else if (tp2.typeSymbol.isRealClass) tp2 - else tp1 - } - } - - /** Does the (possibly generic) type `tp` have the same erasure in all its - * possible instantiations? - */ - def hasStableErasure(tp: Type)(implicit ctx: Context): Boolean = tp match { - case tp: TypeRef => - tp.info match { - case TypeAlias(alias) => hasStableErasure(alias) - case _: ClassInfo => true - case _ => false - } - case tp: PolyParam => false - case tp: TypeProxy => hasStableErasure(tp.superType) - case tp: AndOrType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) - case _ => false - } -} -import TypeErasure._ - -/** - * @param isJava Arguments should be treated the way Java does it - * @param semiEraseVCs If true, value classes are semi-erased to ErasedValueType - * (they will be fully erased in [[ElimErasedValueType]]). - * If false, they are erased like normal classes. - * @param isConstructor Argument forms part of the type of a constructor - * @param wildcardOK Wildcards are acceptable (true when using the erasure - * for computing a signature name). - */ -class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean, wildcardOK: Boolean) extends DotClass { - - /** The erasure |T| of a type T. This is: - * - * - For a refined type scala.Array+[T]: - * - if T is Nothing or Null, []Object - * - otherwise, if T <: Object, []|T| - * - otherwise, if T is a type paramter coming from Java, []Object - * - otherwise, Object - * - For a term ref p.x, the type <noprefix> # x. - * - For a typeref scala.Any, scala.AnyVal or scala.Singleton: |java.lang.Object| - * - For a typeref scala.Unit, |scala.runtime.BoxedUnit|. - * - For a typeref P.C where C refers to a class, <noprefix> # C. - * - For a typeref P.C where C refers to an alias type, the erasure of C's alias. - * - For a typeref P.C where C refers to an abstract type, the erasure of C's upper bound. - * - For a this-type C.this, the type itself. - * - For all other type proxies: The erasure of the underlying type. - * - For T1 & T2, the erased glb of |T1| and |T2| (see erasedGlb) - * - For T1 | T2, the first base class in the linearization of T which is also a base class of T2 - * - For => T, ()T - * - For a method type (Fs)scala.Unit, (|Fs|)scala.Unit. - * - For any other uncurried method type (Fs)T, (|Fs|)|T|. - * - For a curried method type (Fs1)(Fs2)T, (|Fs1|,Es2)ET where (Es2)ET = |(Fs2)T|. - * - For a polymorphic type [Ts](Ps)T, |(Ps)T| - * _ For a polymorphic type [Ts]T where T is not a method type, ()|T| - * - For the class info type of java.lang.Object, the same type without any parents. - * - For a class info type of a value class, the same type without any parents. - * - For any other class info type with parents Ps, the same type with - * parents |Ps|, but with duplicate references of Object removed. - * - For NoType or NoPrefix, the type itself. - * - For any other type, exception. - */ - private def apply(tp: Type)(implicit ctx: Context): Type = tp match { - case _: ErasedValueType => - tp - case tp: TypeRef => - val sym = tp.symbol - if (!sym.isClass) this(tp.info) - else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) - else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. - else eraseNormalClassRef(tp) - case tp: RefinedType => - val parent = tp.parent - if (parent isRef defn.ArrayClass) eraseArray(tp) - else this(parent) - case _: TermRef | _: ThisType => - this(tp.widen) - case SuperType(thistpe, supertpe) => - SuperType(this(thistpe), this(supertpe)) - case ExprType(rt) => - defn.FunctionClass(0).typeRef - case AndType(tp1, tp2) => - erasedGlb(this(tp1), this(tp2), isJava) - case OrType(tp1, tp2) => - ctx.typeComparer.orType(this(tp1), this(tp2), erased = true) - case tp: MethodType => - def paramErasure(tpToErase: Type) = - erasureFn(tp.isJava, semiEraseVCs, isConstructor, wildcardOK)(tpToErase) - val formals = tp.paramTypes.mapConserve(paramErasure) - eraseResult(tp.resultType) match { - case rt: MethodType => - tp.derivedMethodType(tp.paramNames ++ rt.paramNames, formals ++ rt.paramTypes, rt.resultType) - case rt => - tp.derivedMethodType(tp.paramNames, formals, rt) - } - case tp @ ClassInfo(pre, cls, classParents, decls, _) => - if (cls is Package) tp - else { - def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef] - val parents: List[TypeRef] = - if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil - else classParents.mapConserve(eraseTypeRef) match { - case tr :: trs1 => - assert(!tr.classSymbol.is(Trait), cls) - val tr1 = if (cls is Trait) defn.ObjectType else tr - tr1 :: trs1.filterNot(_ isRef defn.ObjectClass) - case nil => nil - } - val erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass) - tp.derivedClassInfo(NoPrefix, parents, erasedDecls, erasedRef(tp.selfType)) - // can't replace selftype by NoType because this would lose the sourceModule link - } - case NoType | NoPrefix | ErrorType | JavaArrayType(_) => - tp - case tp: WildcardType if wildcardOK => - tp - case tp: TypeProxy => - this(tp.underlying) - } - - private def eraseArray(tp: RefinedType)(implicit ctx: Context) = { - val defn.ArrayOf(elemtp) = tp - def arrayErasure(tpToErase: Type) = - erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(tpToErase) - if (elemtp derivesFrom defn.NullClass) JavaArrayType(defn.ObjectType) - else if (isUnboundedGeneric(elemtp) && !isJava) defn.ObjectType - else JavaArrayType(arrayErasure(elemtp)) - } - - /** The erasure of a symbol's info. This is different from `apply` in the way `ExprType`s and - * `PolyType`s are treated. `eraseInfo` maps them them to method types, whereas `apply` maps them - * to the underlying type. - */ - def eraseInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { - case ExprType(rt) => - if (sym is Param) apply(tp) - // Note that params with ExprTypes are eliminated by ElimByName, - // but potentially re-introduced by ResolveSuper, when we add - // forwarders to mixin methods. - // See doc comment for ElimByName for speculation how we could improve this. - else MethodType(Nil, Nil, eraseResult(rt)) - case tp: PolyType => - eraseResult(tp.resultType) match { - case rt: MethodType => rt - case rt => MethodType(Nil, Nil, rt) - } - case tp => this(tp) - } - - private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = { - val cls = tref.symbol.asClass - val underlying = underlyingOfValueClass(cls) - if (underlying.exists) ErasedValueType(tref, valueErasure(underlying)) - else NoType - } - - private def eraseNormalClassRef(tref: TypeRef)(implicit ctx: Context): Type = { - val cls = tref.symbol.asClass - (if (cls.owner is Package) normalizeClass(cls) else cls).typeRef - } - - /** The erasure of a function result type. */ - private def eraseResult(tp: Type)(implicit ctx: Context): Type = tp match { - case tp: TypeRef => - val sym = tp.typeSymbol - if (sym eq defn.UnitClass) sym.typeRef - // For a value class V, "new V(x)" should have type V for type adaptation to work - // correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the return type of a - // constructor method should not be semi-erased. - else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp) - else this(tp) - case RefinedType(parent, _, _) if !(parent isRef defn.ArrayClass) => - eraseResult(parent) - case _ => - this(tp) - } - - private def normalizeClass(cls: ClassSymbol)(implicit ctx: Context): ClassSymbol = { - if (cls.owner == defn.ScalaPackageClass) { - if (cls == defn.AnyClass || cls == defn.AnyValClass || cls == defn.SingletonClass) - return defn.ObjectClass - if (cls == defn.UnitClass) - return defn.BoxedUnitClass - } - cls - } - - /** The name of the type as it is used in `Signature`s. - * Need to ensure correspondence with erasure! - */ - private def sigName(tp: Type)(implicit ctx: Context): TypeName = try { - tp match { - case ErasedValueType(_, underlying) => - sigName(underlying) - case tp: TypeRef => - if (!tp.denot.exists) throw new MissingType(tp.prefix, tp.name) - val sym = tp.symbol - if (!sym.isClass) { - val info = tp.info - if (!info.exists) assert(false, "undefined: $tp with symbol $sym") - return sigName(info) - } - if (isDerivedValueClass(sym)) { - val erasedVCRef = eraseDerivedValueClassRef(tp) - if (erasedVCRef.exists) return sigName(erasedVCRef) - } - normalizeClass(sym.asClass).fullName.asTypeName - case defn.ArrayOf(elem) => - sigName(this(tp)) - case JavaArrayType(elem) => - sigName(elem) ++ "[]" - case tp: TermRef => - sigName(tp.widen) - case ExprType(rt) => - sigName(defn.FunctionOf(Nil, rt)) - case tp: TypeVar => - val inst = tp.instanceOpt - if (inst.exists) sigName(inst) else tpnme.Uninstantiated - case tp: TypeProxy => - sigName(tp.underlying) - case ErrorType | WildcardType => - tpnme.WILDCARD - case tp: WildcardType => - sigName(tp.optBounds) - case _ => - val erased = this(tp) - assert(erased ne tp, tp) - sigName(erased) - } - } catch { - case ex: AssertionError => - println(s"no sig for $tp") - throw ex - } - - -} diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala deleted file mode 100644 index 92e5f9d57..000000000 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ /dev/null @@ -1,554 +0,0 @@ -package dotty.tools -package dotc -package core - -import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._ -import SymDenotations._, Denotations.SingleDenotation -import config.Printers.typr -import util.Positions._ -import NameOps._ -import Decorators._ -import StdNames._ -import Annotations._ -import util.SimpleMap -import collection.mutable -import ast.tpd._ - -trait TypeOps { this: Context => // TODO: Make standalone object. - - /** The type `tp` as seen from prefix `pre` and owner `cls`. See the spec - * for what this means. Called very often, so the code is optimized heavily. - * - * A tricky aspect is what to do with unstable prefixes. E.g. say we have a class - * - * class C { type T; def f(x: T): T } - * - * and an expression `e` of type `C`. Then computing the type of `e.f` leads - * to the query asSeenFrom(`C`, `(x: T)T`). What should its result be? The - * naive answer `(x: C#T)C#T` is incorrect given that we treat `C#T` as the existential - * `exists(c: C)c.T`. What we need to do instead is to skolemize the existential. So - * the answer would be `(x: c.T)c.T` for some (unknown) value `c` of type `C`. - * `c.T` is expressed in the compiler as a skolem type `Skolem(C)`. - * - * Now, skolemization is messy and expensive, so we want to do it only if we absolutely - * must. Also, skolemizing immediately would mean that asSeenFrom was no longer - * idempotent - each call would return a type with a different skolem. - * Instead we produce an annotated type that marks the prefix as unsafe: - * - * (x: (C @ UnsafeNonvariant)#T)C#T - * - * We also set a global state flag `unsafeNonvariant` to the current run. - * When typing a Select node, typer will check that flag, and if it - * points to the current run will scan the result type of the select for - * @UnsafeNonvariant annotations. If it finds any, it will introduce a skolem - * constant for the prefix and try again. - * - * The scheme is efficient in particular because we expect that unsafe situations are rare; - * most compiles would contain none, so no scanning would be necessary. - */ - final def asSeenFrom(tp: Type, pre: Type, cls: Symbol): Type = - asSeenFrom(tp, pre, cls, null) - - /** Helper method, taking a map argument which is instantiated only for more - * complicated cases of asSeenFrom. - */ - private def asSeenFrom(tp: Type, pre: Type, cls: Symbol, theMap: AsSeenFromMap): Type = { - - /** Map a `C.this` type to the right prefix. If the prefix is unstable and - * the `C.this` occurs in nonvariant or contravariant position, mark the map - * to be unstable. - */ - def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"toPrefix($pre, $cls, $thiscls)") /*<|<*/ { - if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass)) - tp - else pre match { - case pre: SuperType => toPrefix(pre.thistpe, cls, thiscls) - case _ => - if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) { - if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre)) { - ctx.base.unsafeNonvariant = ctx.runId - AnnotatedType(pre, Annotation(defn.UnsafeNonvariantAnnot, Nil)) - } - else pre - } - else if ((pre.termSymbol is Package) && !(thiscls is Package)) - toPrefix(pre.select(nme.PACKAGE), cls, thiscls) - else - toPrefix(pre.baseTypeRef(cls).normalizedPrefix, cls.owner, thiscls) - } - } - - /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG - tp match { - case tp: NamedType => - val sym = tp.symbol - if (sym.isStatic) tp - else { - val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap) - if (pre1.isUnsafeNonvariant) - pre1.member(tp.name).info match { - case TypeAlias(alias) => - // try to follow aliases of this will avoid skolemization. - return alias - case _ => - } - tp.derivedSelect(pre1) - } - case tp: ThisType => - toPrefix(pre, cls, tp.cls) - case _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType( - asSeenFrom(tp.parent, pre, cls, theMap), - tp.refinedName, - asSeenFrom(tp.refinedInfo, pre, cls, theMap)) - case tp: TypeAlias if tp.variance == 1 => // if variance != 1, need to do the variance calculation - tp.derivedTypeAlias(asSeenFrom(tp.alias, pre, cls, theMap)) - case _ => - (if (theMap != null) theMap else new AsSeenFromMap(pre, cls)) - .mapOver(tp) - } - } - } - - private def isLegalPrefix(pre: Type)(implicit ctx: Context) = - pre.isStable || !ctx.phase.isTyper - - /** The TypeMap handling the asSeenFrom in more complicated cases */ - class AsSeenFromMap(pre: Type, cls: Symbol) extends TypeMap { - def apply(tp: Type) = asSeenFrom(tp, pre, cls, this) - - /** A method to export the current variance of the map */ - def currentVariance = variance - } - - /** Approximate a type `tp` with a type that does not contain skolem types. */ - object deskolemize extends ApproximatingTypeMap { - private var seen: Set[SkolemType] = Set() - def apply(tp: Type) = tp match { - case tp: SkolemType => - if (seen contains tp) NoType - else { - val saved = seen - seen += tp - try approx(hi = tp.info) - finally seen = saved - } - case _ => - mapOver(tp) - } - } - - /** Implementation of Types#simplified */ - final def simplify(tp: Type, theMap: SimplifyMap): Type = tp match { - case tp: NamedType => - if (tp.symbol.isStatic) tp - else tp.derivedSelect(simplify(tp.prefix, theMap)) match { - case tp1: NamedType if tp1.denotationIsCurrent => - val tp2 = tp1.reduceProjection - //if (tp2 ne tp1) println(i"simplified $tp1 -> $tp2") - tp2 - case tp1 => tp1 - } - case tp: PolyParam => - typerState.constraint.typeVarOfParam(tp) orElse tp - case _: ThisType | _: BoundType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(simplify(tp.parent, theMap), tp.refinedName, simplify(tp.refinedInfo, theMap)) - case tp: TypeAlias => - tp.derivedTypeAlias(simplify(tp.alias, theMap)) - case AndType(l, r) => - simplify(l, theMap) & simplify(r, theMap) - case OrType(l, r) => - simplify(l, theMap) | simplify(r, theMap) - case _ => - (if (theMap != null) theMap else new SimplifyMap).mapOver(tp) - } - - class SimplifyMap extends TypeMap { - def apply(tp: Type) = simplify(tp, this) - } - - /** Approximate union type by intersection of its dominators. - * That is, replace a union type Tn | ... | Tn - * by the smallest intersection type of base-class instances of T1,...,Tn. - * Example: Given - * - * trait C[+T] - * trait D - * class A extends C[A] with D - * class B extends C[B] with D with E - * - * we approximate `A | B` by `C[A | B] with D` - */ - def orDominator(tp: Type): Type = { - - /** a faster version of cs1 intersect cs2 */ - def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = { - val cs2AsSet = new util.HashSet[ClassSymbol](100) - cs2.foreach(cs2AsSet.addEntry) - cs1.filter(cs2AsSet.contains) - } - - /** The minimal set of classes in `cs` which derive all other classes in `cs` */ - def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match { - case c :: rest => - val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu - if (cs == c.baseClasses) accu1 else dominators(rest, accu1) - } - - def mergeRefined(tp1: Type, tp2: Type): Type = { - def fail = throw new AssertionError(i"Failure to join alternatives $tp1 and $tp2") - tp1 match { - case tp1 @ RefinedType(parent1, name1, rinfo1) => - tp2 match { - case RefinedType(parent2, `name1`, rinfo2) => - tp1.derivedRefinedType( - mergeRefined(parent1, parent2), name1, rinfo1 | rinfo2) - case _ => fail - } - case tp1 @ TypeRef(pre1, name1) => - tp2 match { - case tp2 @ TypeRef(pre2, `name1`) => - tp1.derivedSelect(pre1 | pre2) - case _ => fail - } - case _ => fail - } - } - - def approximateOr(tp1: Type, tp2: Type): Type = { - def isClassRef(tp: Type): Boolean = tp match { - case tp: TypeRef => tp.symbol.isClass - case tp: RefinedType => isClassRef(tp.parent) - case _ => false - } - - tp1 match { - case tp1: RecType => - tp1.rebind(approximateOr(tp1.parent, tp2)) - case tp1: TypeProxy if !isClassRef(tp1) => - orDominator(tp1.superType | tp2) - case _ => - tp2 match { - case tp2: RecType => - tp2.rebind(approximateOr(tp1, tp2.parent)) - case tp2: TypeProxy if !isClassRef(tp2) => - orDominator(tp1 | tp2.superType) - case _ => - val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect) - val doms = dominators(commonBaseClasses, Nil) - def baseTp(cls: ClassSymbol): Type = { - val base = - if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls) - else tp.baseTypeWithArgs(cls) - base.mapReduceOr(identity)(mergeRefined) - } - doms.map(baseTp).reduceLeft(AndType.apply) - } - } - } - - tp match { - case tp: OrType => - approximateOr(tp.tp1, tp.tp2) - case _ => - tp - } - } - - /** Given a disjunction T1 | ... | Tn of types with potentially embedded - * type variables, constrain type variables further if this eliminates - * some of the branches of the disjunction. Do this also for disjunctions - * embedded in intersections, as parents in refinements, and in recursive types. - * - * For instance, if `A` is an unconstrained type variable, then - * - * ArrayBuffer[Int] | ArrayBuffer[A] - * - * is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]` - * instead of `ArrayBuffer[_ >: Int | A <: Int & A]` - */ - def harmonizeUnion(tp: Type): Type = tp match { - case tp: OrType => - joinIfScala2(typeComparer.fluidly(tp.tp1 | tp.tp2)) - case tp @ AndType(tp1, tp2) => - tp derived_& (harmonizeUnion(tp1), harmonizeUnion(tp2)) - case tp: RefinedType => - tp.derivedRefinedType(harmonizeUnion(tp.parent), tp.refinedName, tp.refinedInfo) - case tp: RecType => - tp.rebind(harmonizeUnion(tp.parent)) - case _ => - tp - } - - /** Under -language:Scala2: Replace or-types with their joins */ - private def joinIfScala2(tp: Type) = tp match { - case tp: OrType if scala2Mode => tp.join - case _ => tp - } - - /** Not currently needed: - * - def liftToRec(f: (Type, Type) => Type)(tp1: Type, tp2: Type)(implicit ctx: Context) = { - def f2(tp1: Type, tp2: Type): Type = tp2 match { - case tp2: RecType => tp2.rebind(f(tp1, tp2.parent)) - case _ => f(tp1, tp2) - } - tp1 match { - case tp1: RecType => tp1.rebind(f2(tp1.parent, tp2)) - case _ => f2(tp1, tp2) - } - } - */ - - private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = { - val lazyInfo = new LazyType { // needed so we do not force `formal`. - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - denot setFlag formal.flags & RetainedTypeArgFlags - denot.info = info - } - } - val sym = ctx.newSymbol( - cls, formal.name, - formal.flagsUNSAFE & RetainedTypeArgFlags | BaseTypeArg | Override, - lazyInfo, - coord = cls.coord) - cls.enter(sym, decls) - } - - /** If `tpe` is of the form `p.x` where `p` refers to a package - * but `x` is not owned by a package, expand it to - * - * p.package.x - */ - def makePackageObjPrefixExplicit(tpe: NamedType): Type = { - def tryInsert(pkgClass: SymDenotation): Type = pkgClass match { - case pkgCls: PackageClassDenotation if !(tpe.symbol.maybeOwner is Package) => - tpe.derivedSelect(pkgCls.packageObj.valRef) - case _ => - tpe - } - tpe.prefix match { - case pre: ThisType if pre.cls is Package => tryInsert(pre.cls) - case pre: TermRef if pre.symbol is Package => tryInsert(pre.symbol.moduleClass) - case _ => tpe - } - } - - /** If we have member definitions - * - * type argSym v= from - * type from v= to - * - * where the variances of both alias are the same, then enter a new definition - * - * type argSym v= to - * - * unless a definition for `argSym` already exists in the current scope. - */ - def forwardRef(argSym: Symbol, from: Symbol, to: TypeBounds, cls: ClassSymbol, decls: Scope) = - argSym.info match { - case info @ TypeBounds(lo2 @ TypeRef(_: ThisType, name), hi2) => - if (name == from.name && - (lo2 eq hi2) && - info.variance == to.variance && - !decls.lookup(argSym.name).exists) { - // println(s"short-circuit ${argSym.name} was: ${argSym.info}, now: $to") - enterArgBinding(argSym, to, cls, decls) - } - case _ => - } - - - /** Normalize a list of parent types of class `cls` that may contain refinements - * to a list of typerefs referring to classes, by converting all refinements to member - * definitions in scope `decls`. Can add members to `decls` as a side-effect. - */ - def normalizeToClassRefs(parents: List[Type], cls: ClassSymbol, decls: Scope): List[TypeRef] = { - - /** If we just entered the type argument binding - * - * type From = To - * - * and there is a type argument binding in a parent in `prefs` of the form - * - * type X = From - * - * then also add the binding - * - * type X = To - * - * to the current scope, provided (1) variances of both aliases are the same, and - * (2) X is not yet defined in current scope. This "short-circuiting" prevents - * long chains of aliases which would have to be traversed in type comparers. - * - * Note: Test i1401.scala shows that `forwardRefs` is also necessary - * for typechecking in the case where self types refer to type parameters - * that are upper-bounded by subclass instances. - */ - def forwardRefs(from: Symbol, to: Type, prefs: List[TypeRef]) = to match { - case to @ TypeBounds(lo1, hi1) if lo1 eq hi1 => - for (pref <- prefs) { - def forward(): Unit = - for (argSym <- pref.decls) - if (argSym is BaseTypeArg) - forwardRef(argSym, from, to, cls, decls) - pref.info match { - case info: TempClassInfo => info.addSuspension(forward) - case _ => forward() - } - } - case _ => - } - - // println(s"normalizing $parents of $cls in ${cls.owner}") // !!! DEBUG - - // A map consolidating all refinements arising from parent type parameters - var refinements: SimpleMap[TypeName, Type] = SimpleMap.Empty - - // A map of all formal type parameters of base classes that get refined - var formals: SimpleMap[TypeName, Symbol] = SimpleMap.Empty // A map of all formal parent parameter - - // Strip all refinements from parent type, populating `refinements` and `formals` maps. - def normalizeToRef(tp: Type): TypeRef = { - def fail = throw new TypeError(s"unexpected parent type: $tp") - tp.dealias match { - case tp: TypeRef => - tp - case tp @ RefinedType(tp1, name: TypeName, rinfo) => - rinfo match { - case TypeAlias(TypeRef(pre, name1)) if name1 == name && (pre =:= cls.thisType) => - // Don't record refinements of the form X = this.X (These can arise using named parameters). - typr.println(s"dropping refinement $tp") - case _ => - val prevInfo = refinements(name) - refinements = refinements.updated(name, - if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo) - formals = formals.updated(name, tp1.typeParamNamed(name)) - } - normalizeToRef(tp1) - case ErrorType => - defn.AnyType - case AnnotatedType(tpe, _) => - normalizeToRef(tpe) - case HKApply(tycon: TypeRef, args) => - tycon.info match { - case TypeAlias(alias) => normalizeToRef(alias.appliedTo(args)) - case _ => fail - } - case _ => - fail - } - } - - val parentRefs = parents map normalizeToRef - - // Enter all refinements into current scope. - refinements foreachBinding { (name, refinedInfo) => - assert(decls.lookup(name) == NoSymbol, // DEBUG - s"redefinition of ${decls.lookup(name).debugString} in ${cls.showLocated}") - enterArgBinding(formals(name), refinedInfo, cls, decls) - } - // Forward definitions in super classes that have one of the refined parameters - // as aliases directly to the refined info. - // Note that this cannot be fused with the previous loop because we now - // assume that all arguments have been entered in `decls`. - refinements foreachBinding { (name, refinedInfo) => - forwardRefs(formals(name), refinedInfo, parentRefs) - } - parentRefs - } - - /** An argument bounds violation is a triple consisting of - * - the argument tree - * - a string "upper" or "lower" indicating which bound is violated - * - the violated bound - */ - type BoundsViolation = (Tree, String, Type) - - /** The list of violations where arguments are not within bounds. - * @param args The arguments - * @param boundss The list of type bounds - * @param instantiate A function that maps a bound type and the list of argument types to a resulting type. - * Needed to handle bounds that refer to other bounds. - */ - def boundsViolations(args: List[Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context): List[BoundsViolation] = { - val argTypes = args.tpes - val violations = new mutable.ListBuffer[BoundsViolation] - for ((arg, bounds) <- args zip boundss) { - def checkOverlapsBounds(lo: Type, hi: Type): Unit = { - //println(i"instantiating ${bounds.hi} with $argTypes") - //println(i" = ${instantiate(bounds.hi, argTypes)}") - val hiBound = instantiate(bounds.hi, argTypes.mapConserve(_.bounds.hi)) - val loBound = instantiate(bounds.lo, argTypes.mapConserve(_.bounds.lo)) - // Note that argTypes can contain a TypeBounds type for arguments that are - // not fully determined. In that case we need to check against the hi bound of the argument. - if (!(lo <:< hiBound)) violations += ((arg, "upper", hiBound)) - if (!(loBound <:< hi)) violations += ((arg, "lower", bounds.lo)) - } - arg.tpe match { - case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi) - case tp => checkOverlapsBounds(tp, tp) - } - } - violations.toList - } - - /** Is `feature` enabled in class `owner`? - * This is the case if one of the following two alternatives holds: - * - * 1. The feature is imported by a named import - * - * import owner.feature - * - * (the feature may be bunched with others, or renamed, but wildcard imports - * don't count). - * - * 2. The feature is enabled by a compiler option - * - * - language:<prefix>feature - * - * where <prefix> is the full name of the owner followed by a "." minus - * the prefix "dotty.language.". - */ - def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = { - def toPrefix(sym: Symbol): String = - if (!sym.exists || (sym eq defn.LanguageModuleClass)) "" - else toPrefix(sym.owner) + sym.name + "." - def featureName = toPrefix(owner) + feature - def hasImport(implicit ctx: Context): Boolean = { - if (ctx.importInfo == null || (ctx.importInfo.site.widen.typeSymbol ne owner)) false - else if (ctx.importInfo.excluded.contains(feature)) false - else if (ctx.importInfo.originals.contains(feature)) true - else { - var c = ctx.outer - while (c.importInfo eq ctx.importInfo) c = c.outer - hasImport(c) - } - } - def hasOption = ctx.base.settings.language.value exists (s => s == featureName || s == "_") - hasImport(ctx.withPhase(ctx.typerPhase)) || hasOption - } - - /** Is auto-tupling enabled? */ - def canAutoTuple = - !featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling) - - def scala2Mode = - featureEnabled(defn.LanguageModuleClass, nme.Scala2) - - def dynamicsEnabled = - featureEnabled(defn.LanguageModuleClass, nme.dynamics) - - def testScala2Mode(msg: String, pos: Position) = { - if (scala2Mode) migrationWarning(msg, pos) - scala2Mode - } -} - -object TypeOps { - @sharable var track = false // !!!DEBUG -} diff --git a/src/dotty/tools/dotc/core/TypeParamInfo.scala b/src/dotty/tools/dotc/core/TypeParamInfo.scala deleted file mode 100644 index 647c895db..000000000 --- a/src/dotty/tools/dotc/core/TypeParamInfo.scala +++ /dev/null @@ -1,40 +0,0 @@ -package dotty.tools.dotc.core - -import Names.TypeName -import Contexts.Context -import Types.{Type, TypeBounds} - -/** A common super trait of Symbol and LambdaParam. - * Used to capture the attributes of type parameters which can be implemented as either. - */ -trait TypeParamInfo { - - /** Is this the info of a type parameter? Will return `false` for symbols - * that are not type parameters. - */ - def isTypeParam(implicit ctx: Context): Boolean - - /** The name of the type parameter */ - def paramName(implicit ctx: Context): TypeName - - /** The info of the type parameter */ - def paramBounds(implicit ctx: Context): TypeBounds - - /** The info of the type parameter as seen from a prefix type. - * For type parameter symbols, this is the `memberInfo` as seen from `prefix`. - * For type lambda parameters, it's the same as `paramBounds` as - * `asSeenFrom` has already been applied to the whole type lambda. - */ - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds - - /** The parameter bounds, or the completer if the type parameter - * is an as-yet uncompleted symbol. - */ - def paramBoundsOrCompleter(implicit ctx: Context): Type - - /** The variance of the type parameter */ - def paramVariance(implicit ctx: Context): Int - - /** A type that refers to the parameter */ - def paramRef(implicit ctx: Context): Type -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala deleted file mode 100644 index 5c476c1cb..000000000 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ /dev/null @@ -1,210 +0,0 @@ -package dotty.tools -package dotc -package core - -import Types._ -import Flags._ -import Contexts._ -import util.{SimpleMap, DotClass} -import reporting._ -import printing.{Showable, Printer} -import printing.Texts._ -import config.Config -import collection.mutable - -class TyperState(r: Reporter) extends DotClass with Showable { - - /** The current reporter */ - def reporter = r - - /** The current constraint set */ - def constraint: Constraint = - new OrderingConstraint(SimpleMap.Empty, SimpleMap.Empty, SimpleMap.Empty) - def constraint_=(c: Constraint)(implicit ctx: Context): Unit = {} - - /** The uninstantiated variables */ - def uninstVars = constraint.uninstVars - - /** The ephemeral flag is set as a side effect if an operation accesses - * the underlying type of a type variable. The reason we need this flag is - * that any such operation is not referentially transparent; it might logically change - * its value at the moment the type variable is instantiated. Caching code needs to - * check the ephemeral flag; If the flag is set during an operation, the result - * of that operation should not be cached. - */ - def ephemeral: Boolean = false - def ephemeral_=(x: Boolean): Unit = () - - /** Gives for each instantiated type var that does not yet have its `inst` field - * set, the instance value stored in the constraint. Storing instances in constraints - * is done only in a temporary way for contexts that may be retracted - * without also retracting the type var as a whole. - */ - def instType(tvar: TypeVar)(implicit ctx: Context): Type = constraint.entry(tvar.origin) match { - case _: TypeBounds => NoType - case tp: PolyParam => - var tvar1 = constraint.typeVarOfParam(tp) - if (tvar1.exists) tvar1 else tp - case tp => tp - } - - /** A fresh typer state with the same constraint as this one. - * @param isCommittable The constraint can be committed to an enclosing context. - */ - def fresh(isCommittable: Boolean): TyperState = this - - /** A fresh type state with the same constraint as this one and the given reporter */ - def withReporter(reporter: Reporter) = new TyperState(reporter) - - /** Commit state so that it gets propagated to enclosing context */ - def commit()(implicit ctx: Context): Unit = unsupported("commit") - - /** The closest ancestor of this typer state (including possibly this typer state itself) - * which is not yet committed, or which does not have a parent. - */ - def uncommittedAncestor: TyperState = this - - /** Make type variable instances permanent by assigning to `inst` field if - * type variable instantiation cannot be retracted anymore. Then, remove - * no-longer needed constraint entries. - */ - def gc()(implicit ctx: Context): Unit = () - - /** Is it allowed to commit this state? */ - def isCommittable: Boolean = false - - /** Can this state be transitively committed until the top-level? */ - def isGlobalCommittable: Boolean = false - - def tryWithFallback[T](op: => T)(fallback: => T)(implicit ctx: Context): T = unsupported("tryWithFallBack") - - override def toText(printer: Printer): Text = "ImmutableTyperState" -} - -class MutableTyperState(previous: TyperState, r: Reporter, override val isCommittable: Boolean) -extends TyperState(r) { - - private var myReporter = r - - override def reporter = myReporter - - private val previousConstraint = previous.constraint - private var myConstraint: Constraint = previousConstraint - - override def constraint = myConstraint - override def constraint_=(c: Constraint)(implicit ctx: Context) = { - if (Config.debugCheckConstraintsClosed && isGlobalCommittable) c.checkClosed() - myConstraint = c - } - - private var myEphemeral: Boolean = previous.ephemeral - - override def ephemeral = myEphemeral - override def ephemeral_=(x: Boolean): Unit = { myEphemeral = x } - - override def fresh(isCommittable: Boolean): TyperState = - new MutableTyperState(this, new StoreReporter(reporter), isCommittable) - - override def withReporter(reporter: Reporter) = - new MutableTyperState(this, reporter, isCommittable) - - override val isGlobalCommittable = - isCommittable && - (!previous.isInstanceOf[MutableTyperState] || previous.isGlobalCommittable) - - private var isCommitted = false - - override def uncommittedAncestor: TyperState = - if (isCommitted) previous.uncommittedAncestor else this - - /** Commit typer state so that its information is copied into current typer state - * In addition (1) the owning state of undetermined or temporarily instantiated - * type variables changes from this typer state to the current one. (2) Variables - * that were temporarily instantiated in the current typer state are permanently - * instantiated instead. - * - * A note on merging: An interesting test case is isApplicableSafe.scala. It turns out that this - * requires a context merge using the new `&' operator. Sequence of actions: - * 1) Typecheck argument in typerstate 1. - * 2) Cache argument. - * 3) Evolve same typer state (to typecheck other arguments, say) - * leading to a different constraint. - * 4) Take typechecked argument in same state. - * - * It turns out that the merge is needed not just for - * isApplicableSafe but also for (e.g. erased-lubs.scala) as well as - * many parts of dotty itself. - */ - override def commit()(implicit ctx: Context) = { - val targetState = ctx.typerState - assert(isCommittable) - targetState.constraint = - if (targetState.constraint eq previousConstraint) constraint - else targetState.constraint & constraint - constraint foreachTypeVar { tvar => - if (tvar.owningState eq this) - tvar.owningState = targetState - } - targetState.ephemeral |= ephemeral - targetState.gc() - reporter.flush() - isCommitted = true - } - - override def gc()(implicit ctx: Context): Unit = { - val toCollect = new mutable.ListBuffer[PolyType] - constraint foreachTypeVar { tvar => - if (!tvar.inst.exists) { - val inst = instType(tvar) - if (inst.exists && (tvar.owningState eq this)) { - tvar.inst = inst - val poly = tvar.origin.binder - if (constraint.isRemovable(poly)) toCollect += poly - } - } - } - for (poly <- toCollect) - constraint = constraint.remove(poly) - } - - /** Try operation `op`; if it produces errors, execute `fallback` with constraint and - * reporter as they were before `op` was executed. This is similar to `typer/tryEither`, - * but with one important difference: Any type variable instantiations produced by `op` - * are persisted even if `op` fails. This is normally not what one wants and therefore - * it is recommended to use - * - * tryEither { implicit ctx => op } { (_, _) => fallBack } - * - * instead of - * - * ctx.tryWithFallback(op)(fallBack) - * - * `tryWithFallback` is only used when an implicit parameter search fails - * and the whole expression is subsequently retype-checked with a Wildcard - * expected type (so as to allow an implicit conversion on the result and - * avoid over-constraining the implicit parameter search). In this case, - * the only type variables that might be falsely instantiated by `op` but - * not by `fallBack` are type variables in the typed expression itself, and - * these will be thrown away and new ones will be created on re-typing. - * So `tryWithFallback` is safe. It is also necessary because without it - * we do not propagate enough instantiation information into the implicit search - * and this might lead to a missing parameter type error. This is exhibited - * at several places in the test suite (for instance in `pos_typers`). - * Overall, this is rather ugly, but despite trying for 2 days I have not - * found a better solution. - */ - override def tryWithFallback[T](op: => T)(fallback: => T)(implicit ctx: Context): T = { - val storeReporter = new StoreReporter(myReporter) - val savedReporter = myReporter - myReporter = storeReporter - val savedConstraint = myConstraint - val result = try op finally myReporter = savedReporter - if (!storeReporter.hasErrors) result - else { - myConstraint = savedConstraint - fallback - } - } - - override def toText(printer: Printer): Text = constraint.toText(printer) -} diff --git a/src/dotty/tools/dotc/core/Types.overflow b/src/dotty/tools/dotc/core/Types.overflow deleted file mode 100644 index 77f1f6fc1..000000000 --- a/src/dotty/tools/dotc/core/Types.overflow +++ /dev/null @@ -1,66 +0,0 @@ -object Types { - class Type { - - /** The non-private symbol with given name in the given class that matches this type. - * @param inClass The class containing the symbol's definition - * @param name The name of the symbol we are looking for - * @param site The base type from which member types are computed - def matchingTermSymbol(inClass: Symbol, name: Name, site: Type)(implicit ctx: Context): Symbol = { - var denot = inClass.info.nonPrivateDecl(name) - if (denot.isTerm) { // types of the same name always match - if (denot.isOverloaded) - denot = denot.atSignature(this.signature) // seems we need two kinds of signatures here - if (!(site.memberInfo(denot.symbol) matches this)) - denot = NoDenotation - } - denot.symbol - } - - final def firstParamTypes: List[Type] = this match { - case mt: MethodType => mt.paramTypes - case pt: PolyType => pt.firstParamTypes - case _ => Nil - } - - /** `tp` is either a type variable or poly param. Returns - * Covariant if all occurrences of `tp` in this type are covariant - * Contravariant if all occurrences of `tp` in this type are contravariant - * Covariant | Contravariant if there are no occurrences of `tp` in this type - * EmptyFlags if `tp` occurs noon-variantly in this type - */ - def varianceOf(tp: Type): FlagSet = ??? - - - } - - class AndType extends Type { - - def derived_& (tp1: Type, tp2: Type)(implicit ctx: Context) = - if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this - else tp1 & tp2 - - } - - class OrType extends Type { - - def derived_| (tp1: Type, tp2: Type)(implicit ctx: Context) = - if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this - else tp1 | tp2 - - } - - class MethodType { - /* probably won't be needed - private var _isVarArgs: Boolean = _ - private var knownVarArgs: Boolean = false - - def isVarArgs(implicit ctx: Context) = { - if (!knownVarArgs) { - _isVarArgs = paramTypes.nonEmpty && paramTypes.last.isRepeatedParam - knownVarArgs = true - } - _isVarArgs - } - */ - } -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala deleted file mode 100644 index 89bc21929..000000000 --- a/src/dotty/tools/dotc/core/Types.scala +++ /dev/null @@ -1,3865 +0,0 @@ -package dotty.tools -package dotc -package core - -import util.common._ -import Symbols._ -import Flags._ -import Names._ -import StdNames._, NameOps._ -import Scopes._ -import Constants._ -import Contexts._ -import Annotations._ -import SymDenotations._ -import Decorators._ -import Denotations._ -import Periods._ -import util.Positions.Position -import util.Stats._ -import util.{DotClass, SimpleMap} -import ast.tpd._ -import ast.TreeTypeMap -import printing.Texts._ -import ast.untpd -import dotty.tools.dotc.transform.Erasure -import printing.Printer -import Hashable._ -import Uniques._ -import collection.{mutable, Seq, breakOut} -import config.Config -import annotation.tailrec -import Flags.FlagSet -import language.implicitConversions -import scala.util.hashing.{ MurmurHash3 => hashing } -import config.Printers.{core, typr, cyclicErrors} - -object Types { - - @sharable private var nextId = 0 - - implicit def eqType: Eq[Type, Type] = Eq - - /** The class of types. - * The principal subclasses and sub-objects are as follows: - * - * Type -+- ProxyType --+- NamedType ----+--- TypeRef - * | | \ - * | +- SingletonType-+-+- TermRef - * | | | - * | | +--- ThisType - * | | +--- SuperType - * | | +--- ConstantType - * | | +--- MethodParam - * | | +----RecThis - * | | +--- SkolemType - * | +- PolyParam - * | +- RefinedOrRecType -+-- RefinedType - * | | -+-- RecType - * | +- HKApply - * | +- TypeBounds - * | +- ExprType - * | +- AnnotatedType - * | +- TypeVar - * | +- PolyType - * | - * +- GroundType -+- AndType - * +- OrType - * +- MethodType -----+- ImplicitMethodType - * | +- JavaMethodType - * +- ClassInfo - * | - * +- NoType - * +- NoPrefix - * +- ErrorType - * +- WildcardType - * - * Note: please keep in sync with copy in `docs/docs/internals/type-system.md`. - */ - abstract class Type extends DotClass with Hashable with printing.Showable { - -// ----- Tests ----------------------------------------------------- - - // debug only: a unique identifier for a type - val uniqId = { - nextId = nextId + 1 -// if (nextId == 19555) -// println("foo") - nextId - } - - /** Is this type different from NoType? */ - def exists: Boolean = true - - /** This type, if it exists, otherwise `that` type */ - def orElse(that: => Type) = if (exists) this else that - - /** Is this type a value type? */ - final def isValueType: Boolean = this.isInstanceOf[ValueType] - - /** Is the is value type or type lambda? */ - final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[PolyType] - - /** Does this type denote a stable reference (i.e. singleton type)? */ - final def isStable(implicit ctx: Context): Boolean = stripTypeVar match { - case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable - case _: SingletonType | NoPrefix => true - case tp: RefinedOrRecType => tp.parent.isStable - case _ => false - } - - /** Is this type a (possibly refined or applied or aliased) type reference - * to the given type symbol? - * @sym The symbol to compare to. It must be a class symbol or abstract type. - * It makes no sense for it to be an alias type because isRef would always - * return false in that case. - */ - def isRef(sym: Symbol)(implicit ctx: Context): Boolean = stripAnnots.stripTypeVar match { - case this1: TypeRef => - this1.info match { // see comment in Namer#typeDefSig - case TypeAlias(tp) => tp.isRef(sym) - case _ => this1.symbol eq sym - } - case this1: RefinedOrRecType => this1.parent.isRef(sym) - case this1: HKApply => this1.superType.isRef(sym) - case _ => false - } - - /** Is this type a (neither aliased nor applied) reference to class `sym`? */ - def isDirectRef(sym: Symbol)(implicit ctx: Context): Boolean = stripTypeVar match { - case this1: TypeRef => - this1.name == sym.name && // avoid forcing info if names differ - (this1.symbol eq sym) - case _ => - false - } - - /** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`? - * Implemented like `isRef`, but follows more types: all type proxies as well as and- and or-types - */ - private[Types] def isTightPrefix(sym: Symbol)(implicit ctx: Context): Boolean = stripTypeVar match { - case tp: NamedType => tp.info.isTightPrefix(sym) - case tp: ClassInfo => tp.cls eq sym - case tp: Types.ThisType => tp.cls eq sym - case tp: TypeProxy => tp.underlying.isTightPrefix(sym) - case tp: AndType => tp.tp1.isTightPrefix(sym) && tp.tp2.isTightPrefix(sym) - case tp: OrType => tp.tp1.isTightPrefix(sym) || tp.tp2.isTightPrefix(sym) - case _ => false - } - - /** Is this type an instance of a non-bottom subclass of the given class `cls`? */ - final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = { - def loop(tp: Type) = tp match { - case tp: TypeRef => - val sym = tp.symbol - if (sym.isClass) sym.derivesFrom(cls) else tp.superType.derivesFrom(cls) - case tp: TypeProxy => - tp.underlying.derivesFrom(cls) - case tp: AndType => - tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls) - case tp: OrType => - tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls) - case tp: JavaArrayType => - cls == defn.ObjectClass - case _ => - false - } - cls == defn.AnyClass || loop(this) - } - - /** Is this type guaranteed not to have `null` as a value? - * For the moment this is only true for modules, but it could - * be refined later. - */ - final def isNotNull(implicit ctx: Context): Boolean = - classSymbol is ModuleClass - - /** Is this type produced as a repair for an error? */ - final def isError(implicit ctx: Context): Boolean = stripTypeVar match { - case ErrorType => true - case tp => (tp.typeSymbol is Erroneous) || (tp.termSymbol is Erroneous) - } - - /** Is some part of this type produced as a repair for an error? */ - final def isErroneous(implicit ctx: Context): Boolean = existsPart(_.isError, forceLazy = false) - - /** Does the type carry an annotation that is an instance of `cls`? */ - final def hasAnnotation(cls: ClassSymbol)(implicit ctx: Context): Boolean = stripTypeVar match { - case AnnotatedType(tp, annot) => (annot matches cls) || (tp hasAnnotation cls) - case _ => false - } - - /** Does this type occur as a part of type `that`? */ - final def occursIn(that: Type)(implicit ctx: Context): Boolean = - that existsPart (this == _) - - /** Is this a type of a repeated parameter? */ - def isRepeatedParam(implicit ctx: Context): Boolean = - typeSymbol eq defn.RepeatedParamClass - - /** Does this type carry an UnsafeNonvariant annotation? */ - final def isUnsafeNonvariant(implicit ctx: Context): Boolean = this match { - case AnnotatedType(_, annot) => annot.symbol == defn.UnsafeNonvariantAnnot - case _ => false - } - - /** Does this type have an UnsafeNonvariant annotation on one of its parts? */ - final def hasUnsafeNonvariant(implicit ctx: Context): Boolean = - new HasUnsafeNonAccumulator().apply(false, this) - - /** Is this the type of a method that has a repeated parameter type as - * last parameter type? - */ - def isVarArgsMethod(implicit ctx: Context): Boolean = this match { - case tp: PolyType => tp.resultType.isVarArgsMethod - case MethodType(_, paramTypes) => paramTypes.nonEmpty && paramTypes.last.isRepeatedParam - case _ => false - } - - /** Is this an alias TypeBounds? */ - def isAlias: Boolean = this.isInstanceOf[TypeAlias] - -// ----- Higher-order combinators ----------------------------------- - - /** Returns true if there is a part of this type that satisfies predicate `p`. - */ - final def existsPart(p: Type => Boolean, forceLazy: Boolean = true)(implicit ctx: Context): Boolean = - new ExistsAccumulator(p, forceLazy).apply(false, this) - - /** Returns true if all parts of this type satisfy predicate `p`. - */ - final def forallParts(p: Type => Boolean)(implicit ctx: Context): Boolean = - !existsPart(!p(_)) - - /** Performs operation on all parts of this type */ - final def foreachPart(p: Type => Unit, stopAtStatic: Boolean = false)(implicit ctx: Context): Unit = - new ForeachAccumulator(p, stopAtStatic).apply((), this) - - /** The parts of this type which are type or term refs */ - final def namedParts(implicit ctx: Context): collection.Set[NamedType] = - namedPartsWith(alwaysTrue) - - /** The parts of this type which are type or term refs and which - * satisfy predicate `p`. - * - * @param p The predicate to satisfy - * @param excludeLowerBounds If set to true, the lower bounds of abstract - * types will be ignored. - */ - def namedPartsWith(p: NamedType => Boolean, excludeLowerBounds: Boolean = false) - (implicit ctx: Context): collection.Set[NamedType] = - new NamedPartsAccumulator(p, excludeLowerBounds).apply(mutable.LinkedHashSet(), this) - - /** Map function `f` over elements of an AndType, rebuilding with function `g` */ - def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match { - case AndType(tp1, tp2) => g(tp1.mapReduceAnd(f)(g), tp2.mapReduceAnd(f)(g)) - case _ => f(this) - } - - /** Map function `f` over elements of an OrType, rebuilding with function `g` */ - final def mapReduceOr[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match { - case OrType(tp1, tp2) => g(tp1.mapReduceOr(f)(g), tp2.mapReduceOr(f)(g)) - case _ => f(this) - } - -// ----- Associated symbols ---------------------------------------------- - - /** The type symbol associated with the type */ - final def typeSymbol(implicit ctx: Context): Symbol = this match { - case tp: TypeRef => tp.symbol - case tp: ClassInfo => tp.cls -// case ThisType(cls) => cls // needed? - case tp: SingletonType => NoSymbol - case tp: TypeProxy => tp.underlying.typeSymbol - case _ => NoSymbol - } - - /** The least class or trait of which this type is a subtype or parameterized - * instance, or NoSymbol if none exists (either because this type is not a - * value type, or because superclasses are ambiguous). - */ - final def classSymbol(implicit ctx: Context): Symbol = this match { - case ConstantType(constant) => - constant.tpe.classSymbol - case tp: TypeRef => - val sym = tp.symbol - if (sym.isClass) sym else tp.superType.classSymbol - case tp: ClassInfo => - tp.cls - case tp: SingletonType => - NoSymbol - case tp: TypeProxy => - tp.underlying.classSymbol - case AndType(l, r) => - val lsym = l.classSymbol - val rsym = r.classSymbol - if (lsym isSubClass rsym) lsym - else if (rsym isSubClass lsym) rsym - else NoSymbol - case OrType(l, r) => // TODO does not conform to spec - val lsym = l.classSymbol - val rsym = r.classSymbol - if (lsym isSubClass rsym) rsym - else if (rsym isSubClass lsym) lsym - else NoSymbol - case _ => - NoSymbol - } - - /** The least (wrt <:<) set of class symbols of which this type is a subtype - */ - final def classSymbols(implicit ctx: Context): List[ClassSymbol] = this match { - case tp: ClassInfo => - tp.cls :: Nil - case tp: TypeRef => - val sym = tp.symbol - if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols - case tp: TypeProxy => - tp.underlying.classSymbols - case AndType(l, r) => - l.classSymbols union r.classSymbols - case OrType(l, r) => - l.classSymbols intersect r.classSymbols // TODO does not conform to spec - case _ => - Nil - } - - /** The term symbol associated with the type */ - final def termSymbol(implicit ctx: Context): Symbol = this match { - case tp: TermRef => tp.symbol - case tp: TypeProxy => tp.underlying.termSymbol - case _ => NoSymbol - } - - /** The base classes of this type as determined by ClassDenotation - * in linearization order, with the class itself as first element. - * For AndTypes/OrTypes, the union/intersection of the operands' baseclasses. - * Inherited by all type proxies. `Nil` for all other types. - */ - final def baseClasses(implicit ctx: Context): List[ClassSymbol] = track("baseClasses") { - this match { - case tp: TypeProxy => - tp.underlying.baseClasses - case tp: ClassInfo => - tp.cls.baseClasses - case AndType(tp1, tp2) => - tp1.baseClasses union tp2.baseClasses - case OrType(tp1, tp2) => - tp1.baseClasses intersect tp2.baseClasses - case _ => Nil - } - } - -// ----- Member access ------------------------------------------------- - - /** The scope of all declarations of this type. - * Defined by ClassInfo, inherited by type proxies. - * Empty scope for all other types. - */ - final def decls(implicit ctx: Context): Scope = this match { - case tp: ClassInfo => - tp.decls - case tp: TypeProxy => - tp.underlying.decls - case _ => - EmptyScope - } - - /** A denotation containing the declaration(s) in this type with the given name. - * The result is either a SymDenotation or a MultiDenotation of SymDenotations. - * The info(s) are the original symbol infos, no translation takes place. - */ - final def decl(name: Name)(implicit ctx: Context): Denotation = track("decl") { - findDecl(name, EmptyFlags) - } - - /** A denotation containing the non-private declaration(s) in this type with the given name */ - final def nonPrivateDecl(name: Name)(implicit ctx: Context): Denotation = track("nonPrivateDecl") { - findDecl(name, Private) - } - - /** A denotation containing the declaration(s) in this type with the given - * name, as seen from prefix type `pre`. Declarations that have a flag - * in `excluded` are omitted. - */ - final def findDecl(name: Name, excluded: FlagSet)(implicit ctx: Context): Denotation = this match { - case tp: ClassInfo => - tp.decls.denotsNamed(name).filterExcluded(excluded).toDenot(NoPrefix) - case tp: TypeProxy => - tp.underlying.findDecl(name, excluded) - case ErrorType => - ctx.newErrorSymbol(classSymbol orElse defn.RootClass, name) - case _ => - NoDenotation - } - - /** The member of this type with the given name */ - final def member(name: Name)(implicit ctx: Context): Denotation = /*>|>*/ track("member") /*<|<*/ { - memberExcluding(name, EmptyFlags) - } - - /** The non-private member of this type with the given name. */ - final def nonPrivateMember(name: Name)(implicit ctx: Context): Denotation = track("nonPrivateMember") { - memberExcluding(name, Flags.Private) - } - - final def memberExcluding(name: Name, excluding: FlagSet)(implicit ctx: Context): Denotation = { - // We need a valid prefix for `asSeenFrom` - val pre = this match { - case tp: ClassInfo => - tp.typeRef - case _ => - widenIfUnstable - } - findMember(name, pre, excluding) - } - - /** Find member of this type with given name and - * produce a denotation that contains the type of the member - * as seen from given prefix `pre`. Exclude all members that have - * flags in `excluded` from consideration. - */ - final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = { - @tailrec def go(tp: Type): Denotation = tp match { - case tp: RefinedType => - if (name eq tp.refinedName) goRefined(tp) else go(tp.parent) - case tp: ThisType => - goThis(tp) - case tp: TypeRef => - tp.denot.findMember(name, pre, excluded) - case tp: TermRef => - go (tp.underlying match { - case mt: MethodType - if mt.paramTypes.isEmpty && (tp.symbol is Stable) => mt.resultType - case tp1 => tp1 - }) - case tp: PolyParam => - goParam(tp) - case tp: RecType => - goRec(tp) - case tp: HKApply => - goApply(tp) - case tp: TypeProxy => - go(tp.underlying) - case tp: ClassInfo => - tp.cls.findMember(name, pre, excluded) - case AndType(l, r) => - goAnd(l, r) - case tp: OrType => - // we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix` - // achieved that by narrowing `pre` to each alternative, but it led to merge errors in - // lots of places. The present strategy is instead of widen `tp` using `join` to be a - // supertype of `pre`. - go(tp.join) - case tp: JavaArrayType => - defn.ObjectType.findMember(name, pre, excluded) - case ErrorType => - ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name) - case _ => - NoDenotation - } - def goRec(tp: RecType) = - if (tp.parent == null) NoDenotation - else { - //println(s"find member $pre . $name in $tp") - - // We have to be careful because we might open the same (wrt eq) recursive type - // twice during findMember which risks picking the wrong prefix in the `substRecThis(rt, pre)` - // call below. To avoid this problem we do a defensive copy of the recursive - // type first. But if we do this always we risk being inefficient and we ran into - // stackoverflows when compiling pos/hk.scala under the refinement encoding - // of hk-types. So we only do a copy if the type - // is visited again in a recursive call to `findMember`, as tracked by `tp.opened`. - // Furthermore, if this happens we mark the original recursive type with `openedTwice` - // which means that we always defensively copy the type in the future. This second - // measure is necessary because findMember calls might be cached, so do not - // necessarily appear in nested order. - // Without the defensive copy, Typer.scala fails to compile at the line - // - // untpd.rename(lhsCore, setterName).withType(setterType), WildcardType) - // - // because the subtype check - // - // ThisTree[Untyped]#ThisTree[Typed] <: Tree[Typed] - // - // fails (in fact it thinks the underlying type of the LHS is `Tree[Untyped]`.) - // - // Without the `openedTwice` trick, Typer.scala fails to Ycheck - // at phase resolveSuper. - val rt = - if (tp.opened) { // defensive copy - tp.openedTwice = true - RecType(rt => tp.parent.substRecThis(tp, RecThis(rt))) - } else tp - rt.opened = true - try go(rt.parent).mapInfo(_.substRecThis(rt, pre)) - finally { - if (!rt.openedTwice) rt.opened = false - } - } - - def goRefined(tp: RefinedType) = { - val pdenot = go(tp.parent) - val rinfo = tp.refinedInfo - if (name.isTypeName) { // simplified case that runs more efficiently - val jointInfo = - if (rinfo.isAlias) rinfo - else if (pdenot.info.isAlias) pdenot.info - else if (ctx.pendingMemberSearches.contains(name)) pdenot.info safe_& rinfo - else - try pdenot.info & rinfo - catch { - case ex: CyclicReference => - // happens for tests/pos/sets.scala. findMember is called from baseTypeRef. - // The & causes a subtype check which calls baseTypeRef again with the same - // superclass. In the observed case, the superclass was Any, and - // the special shortcut for Any in derivesFrom was as yet absent. To reproduce, - // remove the special treatment of Any in derivesFrom and compile - // sets.scala. - pdenot.info safe_& rinfo - } - pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo) - } else { - pdenot & ( - new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), - pre, - safeIntersection = ctx.pendingMemberSearches.contains(name)) - } - } - - def goApply(tp: HKApply) = tp.tycon match { - case tl: PolyType => - go(tl.resType).mapInfo(info => - tl.derivedLambdaAbstraction(tl.paramNames, tl.paramBounds, info).appliedTo(tp.args)) - case _ => - go(tp.superType) - } - - def goThis(tp: ThisType) = { - val d = go(tp.underlying) - if (d.exists) - if ((pre eq tp) && d.symbol.is(NamedTypeParam) && (d.symbol.owner eq tp.cls)) - // If we look for a named type parameter `P` in `C.this.P`, looking up - // the fully applied self type of `C` will give as an info the alias type - // `P = this.P`. We need to return a denotation with the underlying bounds instead. - d.symbol.denot - else d - else - // There is a special case to handle: - // trait Super { this: Sub => private class Inner {} println(this.Inner) } - // class Sub extends Super - // When resolving Super.this.Inner, the normal logic goes to the self type and - // looks for Inner from there. But this fails because Inner is private. - // We fix the problem by having the following fallback case, which links up the - // member in Super instead of Sub. - // As an example of this in the wild, see - // loadClassWithPrivateInnerAndSubSelf in ShowClassTests - go(tp.cls.typeRef) orElse d - } - def goParam(tp: PolyParam) = { - val next = tp.underlying - ctx.typerState.constraint.entry(tp) match { - case bounds: TypeBounds if bounds ne next => - ctx.typerState.ephemeral = true - go(bounds.hi) - case _ => - go(next) - } - } - def goAnd(l: Type, r: Type) = { - go(l) & (go(r), pre, safeIntersection = ctx.pendingMemberSearches.contains(name)) - } - - { val recCount = ctx.findMemberCount + 1 - ctx.findMemberCount = recCount - if (recCount >= Config.LogPendingFindMemberThreshold) - ctx.pendingMemberSearches = name :: ctx.pendingMemberSearches - } - - //assert(ctx.findMemberCount < 20) - try go(this) - catch { - case ex: Throwable => - core.println(i"findMember exception for $this member $name, pre = $pre") - throw ex // DEBUG - } - finally { - val recCount = ctx.findMemberCount - if (recCount >= Config.LogPendingFindMemberThreshold) - ctx.pendingMemberSearches = ctx.pendingMemberSearches.tail - ctx.findMemberCount = recCount - 1 - } - } - - /** The set of names of members of this type that pass the given name filter - * when seen as members of `pre`. More precisely, these are all - * of members `name` such that `keepOnly(pre, name)` is `true`. - * @note: OK to use a Set[Name] here because Name hashcodes are replayable, - * hence the Set will always give the same names in the same order. - */ - final def memberNames(keepOnly: NameFilter, pre: Type = this)(implicit ctx: Context): Set[Name] = this match { - case tp: ClassInfo => - tp.cls.memberNames(keepOnly) filter (keepOnly(pre, _)) - case tp: RefinedType => - val ns = tp.parent.memberNames(keepOnly, pre) - if (keepOnly(pre, tp.refinedName)) ns + tp.refinedName else ns - case tp: TypeProxy => - tp.underlying.memberNames(keepOnly, pre) - case tp: AndType => - tp.tp1.memberNames(keepOnly, pre) | tp.tp2.memberNames(keepOnly, pre) - case tp: OrType => - tp.tp1.memberNames(keepOnly, pre) & tp.tp2.memberNames(keepOnly, pre) - case _ => - Set() - } - - def memberDenots(keepOnly: NameFilter, f: (Name, mutable.Buffer[SingleDenotation]) => Unit)(implicit ctx: Context): Seq[SingleDenotation] = { - val buf = mutable.ArrayBuffer[SingleDenotation]() - for (name <- memberNames(keepOnly)) f(name, buf) - buf - } - - /** The set of abstract term members of this type. */ - final def abstractTermMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTermMembers") { - memberDenots(abstractTermNameFilter, - (name, buf) => buf ++= nonPrivateMember(name).altsWith(_ is Deferred)) - } - - /** The set of abstract type members of this type. */ - final def abstractTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTypeMembers") { - memberDenots(abstractTypeNameFilter, - (name, buf) => buf += nonPrivateMember(name).asSingleDenotation) - } - - /** The set of abstract type members of this type. */ - final def nonClassTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("nonClassTypeMembers") { - memberDenots(nonClassTypeNameFilter, - (name, buf) => buf += member(name).asSingleDenotation) - } - - /** The set of type members of this type */ - final def typeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("typeMembers") { - memberDenots(typeNameFilter, - (name, buf) => buf += member(name).asSingleDenotation) - } - - /** The set of implicit members of this type */ - final def implicitMembers(implicit ctx: Context): List[TermRef] = track("implicitMembers") { - memberDenots(implicitFilter, - (name, buf) => buf ++= member(name).altsWith(_ is Implicit)) - .toList.map(d => TermRef.withSig(this, d.symbol.asTerm)) - } - - /** The set of member classes of this type */ - final def memberClasses(implicit ctx: Context): Seq[SingleDenotation] = track("implicitMembers") { - memberDenots(typeNameFilter, - (name, buf) => buf ++= member(name).altsWith(x => x.isClass)) - } - - final def fields(implicit ctx: Context): Seq[SingleDenotation] = track("fields") { - memberDenots(fieldFilter, - (name, buf) => buf ++= member(name).altsWith(x => !x.is(Method))) - } - - /** The set of members of this type having at least one of `requiredFlags` but none of `excludedFlags` set */ - final def membersBasedOnFlags(requiredFlags: FlagSet, excludedFlags: FlagSet)(implicit ctx: Context): Seq[SingleDenotation] = track("implicitMembers") { - memberDenots(takeAllFilter, - (name, buf) => buf ++= memberExcluding(name, excludedFlags).altsWith(x => x.is(requiredFlags))) - } - - /** The info of `sym`, seen as a member of this type. */ - final def memberInfo(sym: Symbol)(implicit ctx: Context): Type = - sym.info.asSeenFrom(this, sym.owner) - - /** This type seen as if it were the type of a member of prefix type `pre` - * declared in class `cls`. - */ - final def asSeenFrom(pre: Type, cls: Symbol)(implicit ctx: Context): Type = track("asSeenFrom") { - if (!cls.membersNeedAsSeenFrom(pre)) this - else ctx.asSeenFrom(this, pre, cls) - } - -// ----- Subtype-related -------------------------------------------- - - /** Is this type a subtype of that type? */ - final def <:<(that: Type)(implicit ctx: Context): Boolean = track("<:<") { - ctx.typeComparer.topLevelSubType(this, that) - } - - /** Is this type a subtype of that type? */ - final def frozen_<:<(that: Type)(implicit ctx: Context): Boolean = track("frozen_<:<") { - ctx.typeComparer.isSubTypeWhenFrozen(this, that) - } - - /** Is this type the same as that type? - * This is the case iff `this <:< that` and `that <:< this`. - */ - final def =:=(that: Type)(implicit ctx: Context): Boolean = track("=:=") { - ctx.typeComparer.isSameType(this, that) - } - - /** Is this type a primitive value type which can be widened to the primitive value type `that`? */ - def isValueSubType(that: Type)(implicit ctx: Context) = widen match { - case self: TypeRef if self.symbol.isPrimitiveValueClass => - that.widenExpr match { - case that: TypeRef if that.symbol.isPrimitiveValueClass => - defn.isValueSubClass(self.symbol, that.symbol) - case _ => - false - } - case _ => - false - } - - def relaxed_<:<(that: Type)(implicit ctx: Context) = - (this <:< that) || (this isValueSubType that) - - /** Is this type a legal type for a member that overrides another - * member of type `that`? This is the same as `<:<`, except that - * the types ()T and => T are identified, and T is seen as overriding - * either type. - */ - final def overrides(that: Type)(implicit ctx: Context) = { - def result(tp: Type): Type = tp match { - case ExprType(_) | MethodType(Nil, _) => tp.resultType - case _ => tp - } - (this frozen_<:< that) || { - val rthat = result(that) - (rthat ne that) && (result(this) frozen_<:< rthat) - } - } - - /** Is this type close enough to that type so that members - * with the two types would override each other? - * This means: - * - Either both types are polytypes with the same number of - * type parameters and their result types match after renaming - * corresponding type parameters - * - Or both types are method types with =:=-equivalent(*) parameter types - * and matching result types after renaming corresponding parameter types - * if the method types are dependent. - * - Or both types are =:=-equivalent - * - Or phase.erasedTypes is false, and neither type takes - * term or type parameters. - * - * (*) when matching with a Java method, we also regard Any and Object as equivalent - * parameter types. - */ - def matches(that: Type)(implicit ctx: Context): Boolean = track("matches") { - ctx.typeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes) - } - - /** This is the same as `matches` except that it also matches => T with T and - * vice versa. - */ - def matchesLoosely(that: Type)(implicit ctx: Context): Boolean = - (this matches that) || { - val thisResult = this.widenExpr - val thatResult = that.widenExpr - (this eq thisResult) != (that eq thatResult) && (thisResult matchesLoosely thatResult) - } - - /** The basetype TypeRef of this type with given class symbol, - * but without including any type arguments - */ - final def baseTypeRef(base: Symbol)(implicit ctx: Context): Type = /*ctx.traceIndented(s"$this baseTypeRef $base")*/ /*>|>*/ track("baseTypeRef") /*<|<*/ { - base.denot match { - case classd: ClassDenotation => classd.baseTypeRefOf(this) - case _ => NoType - } - } - - def & (that: Type)(implicit ctx: Context): Type = track("&") { - ctx.typeComparer.glb(this, that) - } - - /** Safer version of `&`. - * - * This version does not simplify the upper bound of the intersection of - * two TypeBounds. The simplification done by `&` requires subtyping checks - * which may end up calling `&` again, in most cases this should be safe - * but because of F-bounded types, this can result in an infinite loop - * (which will be masked unless `-Yno-deep-subtypes` is enabled). - */ - def safe_& (that: Type)(implicit ctx: Context): Type = (this, that) match { - case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2)) - case _ => this & that - } - - def | (that: Type)(implicit ctx: Context): Type = track("|") { - ctx.typeComparer.lub(this, that) - } - -// ----- Unwrapping types ----------------------------------------------- - - /** Map a TypeVar to either its instance if it is instantiated, or its origin, - * if not, until the result is no longer a TypeVar. Identity on all other types. - */ - def stripTypeVar(implicit ctx: Context): Type = this - - /** Remove all AnnotatedTypes wrapping this type. - */ - def stripAnnots(implicit ctx: Context): Type = this - - /** Widen from singleton type to its underlying non-singleton - * base type by applying one or more `underlying` dereferences, - * Also go from => T to T. - * Identity for all other types. Example: - * - * class Outer { class C ; val x: C } - * def o: Outer - * <o.x.type>.widen = o.C - */ - final def widen(implicit ctx: Context): Type = widenSingleton match { - case tp: ExprType => tp.resultType.widen - case tp => tp - } - - /** Widen from singleton type to its underlying non-singleton - * base type by applying one or more `underlying` dereferences. - */ - final def widenSingleton(implicit ctx: Context): Type = stripTypeVar match { - case tp: SingletonType if !tp.isOverloaded => tp.underlying.widenSingleton - case _ => this - } - - /** Widen from TermRef to its underlying non-termref - * base type, while also skipping Expr types. - */ - final def widenTermRefExpr(implicit ctx: Context): Type = stripTypeVar match { - case tp: TermRef if !tp.isOverloaded => tp.underlying.widenExpr.widenTermRefExpr - case _ => this - } - - /** Widen from ExprType type to its result type. - * (Note: no stripTypeVar needed because TypeVar's can't refer to ExprTypes.) - */ - final def widenExpr: Type = this match { - case tp: ExprType => tp.resType - case _ => this - } - - /** Widen type if it is unstable (i.e. an ExprType, or TermRef to unstable symbol */ - final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match { - case tp: ExprType => tp.resultType.widenIfUnstable - case tp: TermRef if !tp.symbol.isStable => tp.underlying.widenIfUnstable - case _ => this - } - - /** If this is a skolem, its underlying type, otherwise the type itself */ - final def widenSkolem(implicit ctx: Context): Type = this match { - case tp: SkolemType => tp.underlying - case _ => this - } - - /** Eliminate anonymous classes */ - final def deAnonymize(implicit ctx: Context): Type = this match { - case tp:TypeRef if tp.symbol.isAnonymousClass => - tp.symbol.asClass.typeRef.asSeenFrom(tp.prefix, tp.symbol.owner) - case tp => tp - } - - private def dealias(keepAnnots: Boolean)(implicit ctx: Context): Type = this match { - case tp: TypeRef => - if (tp.symbol.isClass) tp - else tp.info match { - case TypeAlias(tp) => tp.dealias(keepAnnots) - case _ => tp - } - case tp: TypeVar => - val tp1 = tp.instanceOpt - if (tp1.exists) tp1.dealias(keepAnnots) else tp - case tp: AnnotatedType => - val tp1 = tp.tpe.dealias(keepAnnots) - if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1 - case tp: LazyRef => - tp.ref.dealias(keepAnnots) - case app @ HKApply(tycon, args) => - val tycon1 = tycon.dealias(keepAnnots) - if (tycon1 ne tycon) app.superType.dealias(keepAnnots) - else this - case _ => this - } - - /** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type - * is no longer alias type, LazyRef, or instantiated type variable. - * Goes through annotated types and rewraps annotations on the result. - */ - final def dealiasKeepAnnots(implicit ctx: Context): Type = - dealias(keepAnnots = true) - - /** Follow aliases and dereferences LazyRefs, annotated types and instantiated - * TypeVars until type is no longer alias type, annotated type, LazyRef, - * or instantiated type variable. - */ - final def dealias(implicit ctx: Context): Type = - dealias(keepAnnots = false) - - /** Perform successive widenings and dealiasings until none can be applied anymore */ - final def widenDealias(implicit ctx: Context): Type = { - val res = this.widen.dealias - if (res eq this) res else res.widenDealias - } - - /** Widen from constant type to its underlying non-constant - * base type. - */ - final def deconst(implicit ctx: Context): Type = stripTypeVar match { - case tp: ConstantType => tp.value.tpe - case _ => this - } - - /** If this is a (possibly aliased, annotated, and/or parameterized) reference to - * a class, the class type ref, otherwise NoType. - * @param refinementOK If `true` we also skip non-parameter refinements. - */ - def underlyingClassRef(refinementOK: Boolean)(implicit ctx: Context): Type = dealias match { - case tp: TypeRef => - if (tp.symbol.isClass) tp - else if (tp.symbol.isAliasType) tp.underlying.underlyingClassRef(refinementOK) - else NoType - case tp: AnnotatedType => - tp.underlying.underlyingClassRef(refinementOK) - case tp: RefinedType => - def isParamName = tp.classSymbol.typeParams.exists(_.name == tp.refinedName) - if (refinementOK || isParamName) tp.underlying.underlyingClassRef(refinementOK) - else NoType - case tp: RecType => - tp.underlying.underlyingClassRef(refinementOK) - case _ => - NoType - } - - /** The iterator of underlying types as long as type is a TypeProxy. - * Useful for diagnostics - */ - def underlyingIterator(implicit ctx: Context): Iterator[Type] = new Iterator[Type] { - var current = Type.this - var hasNext = true - def next = { - val res = current - hasNext = current.isInstanceOf[TypeProxy] - if (hasNext) current = current.asInstanceOf[TypeProxy].underlying - res - } - } - - /** A prefix-less refined this or a termRef to a new skolem symbol - * that has the given type as info. - */ - def narrow(implicit ctx: Context): TermRef = - TermRef(NoPrefix, ctx.newSkolem(this)) - - /** Useful for diagnostics: The underlying type if this type is a type proxy, - * otherwise NoType - */ - def underlyingIfProxy(implicit ctx: Context) = this match { - case this1: TypeProxy => this1.underlying - case _ => NoType - } - - /** If this is a FunProto or PolyProto, WildcardType, otherwise this. */ - def notApplied: Type = this - - // ----- Normalizing typerefs over refined types ---------------------------- - - /** If this normalizes* to a refinement type that has a refinement for `name` (which might be followed - * by other refinements), and the refined info is a type alias, return the alias, - * otherwise return NoType. Used to reduce types of the form - * - * P { ... type T = / += / -= U ... } # T - * - * to just U. Does not perform the reduction if the resulting type would contain - * a reference to the "this" of the current refined type, except in the following situation - * - * (1) The "this" reference can be avoided by following an alias. Example: - * - * P { type T = String, type R = P{...}.T } # R --> String - * - * (*) normalizes means: follow instantiated typevars and aliases. - */ - def lookupRefined(name: Name)(implicit ctx: Context): Type = { - def loop(pre: Type): Type = pre.stripTypeVar match { - case pre: RefinedType => - pre.refinedInfo match { - case TypeAlias(alias) => - if (pre.refinedName ne name) loop(pre.parent) else alias - case _ => loop(pre.parent) - } - case pre: RecType => - val candidate = loop(pre.parent) - if (candidate.exists && !pre.isReferredToBy(candidate)) { - //println(s"lookupRefined ${this.toString} . $name, pre: $pre ---> $candidate / ${candidate.toString}") - candidate - } - else NoType - case SkolemType(tp) => - tp.lookupRefined(name) - case pre: WildcardType => - WildcardType - case pre: TypeRef => - pre.info match { - case TypeAlias(alias) => loop(alias) - case _ => NoType - } - case _ => - NoType - } - - loop(this) - } - - /** The type <this . name> , reduced if possible */ - def select(name: Name)(implicit ctx: Context): Type = name match { - case name: TermName => TermRef.all(this, name) - case name: TypeName => TypeRef(this, name).reduceProjection - } - - /** The type <this . name> , reduced if possible, with given denotation if unreduced */ - def select(name: Name, denot: Denotation)(implicit ctx: Context): Type = name match { - case name: TermName => TermRef(this, name, denot) - case name: TypeName => TypeRef(this, name, denot).reduceProjection - } - - /** The type <this . name> with given symbol, reduced if possible */ - def select(sym: Symbol)(implicit ctx: Context): Type = - if (sym.isTerm) TermRef(this, sym.asTerm) - else TypeRef(this, sym.asType).reduceProjection - -// ----- Access to parts -------------------------------------------- - - /** The normalized prefix of this type is: - * For an alias type, the normalized prefix of its alias - * For all other named type and class infos: the prefix. - * Inherited by all other type proxies. - * `NoType` for all other types. - */ - final def normalizedPrefix(implicit ctx: Context): Type = this match { - case tp: NamedType => - if (tp.symbol.info.isAlias) tp.info.normalizedPrefix else tp.prefix - case tp: ClassInfo => - tp.prefix - case tp: TypeProxy => - tp.underlying.normalizedPrefix - case _ => - NoType - } - - /** For a ClassInfo type, its parents, - * Inherited by all type proxies. Empty for all other types. - * Overwritten in ClassInfo, where parents is cached. - */ - def parents(implicit ctx: Context): List[TypeRef] = this match { - case tp: TypeProxy => tp.underlying.parents - case _ => List() - } - - /** The full parent types, including all type arguments */ - def parentsWithArgs(implicit ctx: Context): List[Type] = this match { - case tp: TypeProxy => tp.superType.parentsWithArgs - case _ => List() - } - - /** The first parent of this type, AnyRef if list of parents is empty */ - def firstParent(implicit ctx: Context): TypeRef = parents match { - case p :: _ => p - case _ => defn.AnyType - } - - /** the self type of the underlying classtype */ - def givenSelfType(implicit ctx: Context): Type = this match { - case tp: RefinedType => tp.wrapIfMember(tp.parent.givenSelfType) - case tp: ThisType => tp.tref.givenSelfType - case tp: TypeProxy => tp.superType.givenSelfType - case _ => NoType - } - - /** The parameter types of a PolyType or MethodType, Empty list for others */ - final def paramTypess(implicit ctx: Context): List[List[Type]] = this match { - case mt: MethodType => mt.paramTypes :: mt.resultType.paramTypess - case pt: PolyType => pt.resultType.paramTypess - case _ => Nil - } - - /** The parameter names of a PolyType or MethodType, Empty list for others */ - final def paramNamess(implicit ctx: Context): List[List[TermName]] = this match { - case mt: MethodType => mt.paramNames :: mt.resultType.paramNamess - case pt: PolyType => pt.resultType.paramNamess - case _ => Nil - } - - - /** The parameter types in the first parameter section of a generic type or MethodType, Empty list for others */ - final def firstParamTypes(implicit ctx: Context): List[Type] = this match { - case mt: MethodType => mt.paramTypes - case pt: PolyType => pt.resultType.firstParamTypes - case _ => Nil - } - - /** Is this either not a method at all, or a parameterless method? */ - final def isParameterless(implicit ctx: Context): Boolean = this match { - case mt: MethodType => false - case pt: PolyType => pt.resultType.isParameterless - case _ => true - } - - /** The resultType of a PolyType, MethodType, or ExprType, the type itself for others */ - def resultType(implicit ctx: Context): Type = this - - /** The final result type of a PolyType, MethodType, or ExprType, after skipping - * all parameter sections, the type itself for all others. - */ - def finalResultType(implicit ctx: Context): Type = resultType match { - case mt: MethodType => mt.resultType.finalResultType - case pt: PolyType => pt.resultType.finalResultType - case _ => resultType - } - - /** This type seen as a TypeBounds */ - final def bounds(implicit ctx: Context): TypeBounds = this match { - case tp: TypeBounds => tp - case ci: ClassInfo => TypeAlias(ci.typeRef) - case wc: WildcardType => - wc.optBounds match { - case bounds: TypeBounds => bounds - case NoType => TypeBounds.empty - } - case _ => TypeAlias(this) - } - - /** The type parameter with given `name`. This tries first `decls` - * in order not to provoke a cycle by forcing the info. If that yields - * no symbol it tries `member` as an alternative. - */ - def typeParamNamed(name: TypeName)(implicit ctx: Context): Symbol = - classSymbol.unforcedDecls.lookup(name) orElse member(name).symbol - - /** If this is a prototype with some ignored component, reveal one more - * layer of it. Otherwise the type itself. - */ - def deepenProto(implicit ctx: Context): Type = this - -// ----- Substitutions ----------------------------------------------------- - - /** Substitute all types that refer in their symbol attribute to - * one of the symbols in `from` by the corresponding types in `to`. - */ - final def subst(from: List[Symbol], to: List[Type])(implicit ctx: Context): Type = - if (from.isEmpty) this - else { - val from1 = from.tail - if (from1.isEmpty) ctx.subst1(this, from.head, to.head, null) - else { - val from2 = from1.tail - if (from2.isEmpty) ctx.subst2(this, from.head, to.head, from1.head, to.tail.head, null) - else ctx.subst(this, from, to, null) - } - } - - /** Same as `subst` but follows aliases as a fallback. When faced with a reference - * to an alias type, where normal substitution does not yield a new type, the - * substitution is instead applied to the alias. If that yields a new type, - * this type is returned, otherwise the original type (not the alias) is returned. - * A use case for this method is if one wants to substitute the type parameters - * of a class and also wants to substitute any parameter accessors that alias - * the type parameters. - */ - final def substDealias(from: List[Symbol], to: List[Type])(implicit ctx: Context): Type = - ctx.substDealias(this, from, to, null) - - /** Substitute all types of the form `PolyParam(from, N)` by - * `PolyParam(to, N)`. - */ - final def subst(from: BindingType, to: BindingType)(implicit ctx: Context): Type = - ctx.subst(this, from, to, null) - - /** Substitute all occurrences of `This(cls)` by `tp` */ - final def substThis(cls: ClassSymbol, tp: Type)(implicit ctx: Context): Type = - ctx.substThis(this, cls, tp, null) - - /** As substThis, but only is class is a static owner (i.e. a globally accessible object) */ - final def substThisUnlessStatic(cls: ClassSymbol, tp: Type)(implicit ctx: Context): Type = - if (cls.isStaticOwner) this else ctx.substThis(this, cls, tp, null) - - /** Substitute all occurrences of `RecThis(binder)` by `tp` */ - final def substRecThis(binder: RecType, tp: Type)(implicit ctx: Context): Type = - ctx.substRecThis(this, binder, tp, null) - - /** Substitute a bound type by some other type */ - final def substParam(from: ParamType, to: Type)(implicit ctx: Context): Type = - ctx.substParam(this, from, to, null) - - /** Substitute bound types by some other types */ - final def substParams(from: BindingType, to: List[Type])(implicit ctx: Context): Type = - ctx.substParams(this, from, to, null) - - /** Substitute all occurrences of symbols in `from` by references to corresponding symbols in `to` - */ - final def substSym(from: List[Symbol], to: List[Symbol])(implicit ctx: Context): Type = - ctx.substSym(this, from, to, null) - -// ----- misc ----------------------------------------------------------- - - /** Turn type into a function type. - * @pre this is a non-dependent method type. - * @param dropLast The number of trailing parameters that should be dropped - * when forming the function type. - */ - def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match { - case mt @ MethodType(_, formals) if !mt.isDependent || ctx.mode.is(Mode.AllowDependentFunctions) => - val formals1 = if (dropLast == 0) formals else formals dropRight dropLast - defn.FunctionOf( - formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType) - } - - /** The signature of this type. This is by default NotAMethod, - * but is overridden for PolyTypes, MethodTypes, and TermRefWithSignature types. - * (the reason why we deviate from the "final-method-with-pattern-match-in-base-class" - * pattern is that method signatures use caching, so encapsulation - * is improved using an OO scheme). - */ - def signature(implicit ctx: Context): Signature = Signature.NotAMethod - - /** Convert to text */ - def toText(printer: Printer): Text = printer.toText(this) - - /** Utility method to show the underlying type of a TypeProxy chain together - * with the proxy type itself. - */ - def showWithUnderlying(n: Int = 1)(implicit ctx: Context): String = this match { - case tp: TypeProxy if n > 0 => s"$show with underlying ${tp.underlying.showWithUnderlying(n - 1)}" - case _ => show - } - - /** A simplified version of this type which is equivalent wrt =:= to this type. - * This applies a typemap to the type which (as all typemaps) follows type - * variable instances and reduces typerefs over refined types. It also - * re-evaluates all occurrences of And/OrType with &/| because - * what was a union or intersection of type variables might be a simpler type - * after the type variables are instantiated. Finally, it - * maps poly params in the current constraint set back to their type vars. - */ - def simplified(implicit ctx: Context) = ctx.simplify(this, null) - - /** customized hash code of this type. - * NotCached for uncached types. Cached types - * compute hash and use it as the type's hashCode. - */ - def hash: Int - } // end Type - -// ----- Type categories ---------------------------------------------- - - /** A marker trait for cached types */ - trait CachedType extends Type - - /** A marker trait for type proxies. - * Each implementation is expected to redefine the `underlying` method. - */ - abstract class TypeProxy extends Type { - - /** The type to which this proxy forwards operations. */ - def underlying(implicit ctx: Context): Type - - /** The closest supertype of this type. This is the same as `underlying`, - * except for TypeRefs where the upper bound is returned, and HKApplys, - * where the upper bound of the constructor is re-applied to the arguments. - */ - def superType(implicit ctx: Context): Type = underlying - } - - // Every type has to inherit one of the following four abstract type classes., - // which determine whether the type is cached, and whether - // it is a proxy of some other type. The duplication in their methods - // is for efficiency. - - /** Instances of this class are cached and are not proxies. */ - abstract class CachedGroundType extends Type with CachedType { - private[this] var myHash = HashUnknown - final def hash = { - if (myHash == HashUnknown) { - myHash = computeHash - assert(myHash != HashUnknown) - } - myHash - } - override final def hashCode = - if (hash == NotCached) System.identityHashCode(this) else hash - def computeHash: Int - } - - /** Instances of this class are cached and are proxies. */ - abstract class CachedProxyType extends TypeProxy with CachedType { - protected[this] var myHash = HashUnknown - final def hash = { - if (myHash == HashUnknown) { - myHash = computeHash - assert(myHash != HashUnknown) - } - myHash - } - override final def hashCode = - if (hash == NotCached) System.identityHashCode(this) else hash - def computeHash: Int - } - - /** Instances of this class are uncached and are not proxies. */ - abstract class UncachedGroundType extends Type { - final def hash = NotCached - if (monitored) { - record(s"uncachable") - record(s"uncachable: $getClass") - } - } - - /** Instances of this class are uncached and are proxies. */ - abstract class UncachedProxyType extends TypeProxy { - final def hash = NotCached - if (monitored) { - record(s"uncachable") - record(s"uncachable: $getClass") - } - } - - /** A marker trait for types that apply only to type symbols */ - trait TypeType extends Type - - /** A marker trait for types that apply only to term symbols or that - * represent higher-kinded types. - */ - trait TermType extends Type - - /** A marker trait for types that can be types of values or prototypes of value types */ - trait ValueTypeOrProto extends TermType - - /** A marker trait for types that can be types of values or that are higher-kinded */ - trait ValueType extends ValueTypeOrProto - - /** A marker trait for types that are guaranteed to contain only a - * single non-null value (they might contain null in addition). - */ - trait SingletonType extends TypeProxy with ValueType { - def isOverloaded(implicit ctx: Context) = false - } - - /** A marker trait for types that bind other types that refer to them. - * Instances are: PolyType, MethodType, RefinedType. - */ - trait BindingType extends Type - - /** A trait for proto-types, used as expected types in typer */ - trait ProtoType extends Type { - def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T - def map(tm: TypeMap)(implicit ctx: Context): ProtoType - } - - /** Implementations of this trait cache the results of `narrow`. */ - trait NarrowCached extends Type { - private var myNarrow: TermRef = null - override def narrow(implicit ctx: Context): TermRef = { - if (myNarrow eq null) myNarrow = super.narrow - myNarrow - } - } - -// --- NamedTypes ------------------------------------------------------------------ - - /** A NamedType of the form Prefix # name */ - abstract class NamedType extends CachedProxyType with ValueType { - - val prefix: Type - val name: Name - - type ThisType >: this.type <: NamedType - - assert(prefix.isValueType || (prefix eq NoPrefix), s"invalid prefix $prefix") - - private[this] var lastDenotation: Denotation = _ - private[this] var lastSymbol: Symbol = _ - private[this] var checkedPeriod = Nowhere - - // Invariants: - // (1) checkedPeriod != Nowhere => lastDenotation != null - // (2) lastDenotation != null => lastSymbol != null - - /** There is a denotation computed which is valid (somewhere in) the - * current run. - */ - def denotationIsCurrent(implicit ctx: Context) = - lastDenotation != null && lastDenotation.validFor.runId == ctx.runId - - /** The denotation is current, its symbol, otherwise NoDenotation. - * - * Note: This operation does not force the denotation, and is therefore - * timing dependent. It should only be used if the outcome of the - * essential computation does not depend on the symbol being present or not. - * It's currently used to take an optimized path in substituters and - * type accumulators, as well as to be safe in diagnostic printing. - * Normally, it's better to use `symbol`, not `currentSymbol`. - */ - def currentSymbol(implicit ctx: Context) = - if (denotationIsCurrent) symbol else NoSymbol - - /** The denotation currently denoted by this type */ - final def denot(implicit ctx: Context): Denotation = { - val now = ctx.period - if (checkedPeriod == now) lastDenotation else denotAt(now) - } - - /** A first fall back to do a somewhat more expensive calculation in case the first - * attempt in `denot` does not yield a denotation. - */ - private def denotAt(now: Period)(implicit ctx: Context): Denotation = { - val d = lastDenotation - if (d != null && (d.validFor contains now)) { - checkedPeriod = now - d - } - else computeDenot - } - - /** Hook for adding debug check code when denotations are assigned */ - final def checkDenot()(implicit ctx: Context) = {} - - /** A second fallback to recompute the denotation if necessary */ - private def computeDenot(implicit ctx: Context): Denotation = { - val savedEphemeral = ctx.typerState.ephemeral - ctx.typerState.ephemeral = false - try { - val d = lastDenotation match { - case null => - val sym = lastSymbol - if (sym == null) loadDenot else denotOfSym(sym) - case d: SymDenotation => - if (this.isInstanceOf[WithFixedSym]) d.current - else if (d.validFor.runId == ctx.runId || ctx.stillValid(d)) - if (d.exists && prefix.isTightPrefix(d.owner) || d.isConstructor) d.current - else recomputeMember(d) // symbol could have been overridden, recompute membership - else { - val newd = loadDenot - if (newd.exists) newd else d.staleSymbolError - } - case d => - if (d.validFor.runId != ctx.period.runId) loadDenot - else d.current - } - if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot") - else if (d.exists) { - // Avoid storing NoDenotations in the cache - we will not be able to recover from - // them. The situation might arise that a type has NoDenotation in some later - // phase but a defined denotation earlier (e.g. a TypeRef to an abstract type - // is undefined after erasure.) We need to be able to do time travel back and - // forth also in these cases. - - // Don't use setDenot here; double binding checks can give spurious failures after erasure - lastDenotation = d - checkDenot() - lastSymbol = d.symbol - checkedPeriod = ctx.period - } - d - } - finally ctx.typerState.ephemeral |= savedEphemeral - } - - /** A member of `prefix` (disambiguated by `d.signature`) or, if none was found, `d.current`. */ - private def recomputeMember(d: SymDenotation)(implicit ctx: Context): Denotation = - asMemberOf(prefix) match { - case NoDenotation => d.current - case newd: SingleDenotation => newd - case newd => - newd.atSignature(d.signature) match { - case newd1: SingleDenotation if newd1.exists => newd1 - case _ => d.current - } - } - - private def denotOfSym(sym: Symbol)(implicit ctx: Context): Denotation = { - val d = sym.denot - val owner = d.owner - if (owner.isTerm) d else d.asSeenFrom(prefix) - } - - private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = { - def selfTypeOf(sym: Symbol) = sym.owner.info match { - case info: ClassInfo => info.givenSelfType - case _ => NoType - } - assert( - (lastSymbol eq sym) || - (lastSymbol eq null) || { - val lastDefRunId = lastDenotation match { - case d: SymDenotation => d.validFor.runId - case _ => lastSymbol.defRunId - } - (lastDefRunId != sym.defRunId) || - (lastDefRunId == NoRunId) - } || - (lastSymbol.infoOrCompleter == ErrorType || - sym.owner != lastSymbol.owner && - (sym.owner.derivesFrom(lastSymbol.owner) || - selfTypeOf(sym).derivesFrom(lastSymbol.owner) || - selfTypeOf(lastSymbol).derivesFrom(sym.owner))), - i"""data race? overwriting symbol of type $this, - |long form = $toString of class $getClass, - |last sym id = ${lastSymbol.id}, new sym id = ${sym.id}, - |last owner = ${lastSymbol.owner}, new owner = ${sym.owner}, - |period = ${ctx.phase} at run ${ctx.runId}""") - } - - protected def sig: Signature = Signature.NotAMethod - - private[dotc] def withDenot(denot: Denotation)(implicit ctx: Context): ThisType = - if (sig != denot.signature) - withSig(denot.signature).withDenot(denot).asInstanceOf[ThisType] - else { - setDenot(denot) - this - } - - private[dotc] final def setDenot(denot: Denotation)(implicit ctx: Context): Unit = { - if (Config.checkNoDoubleBindings) - if (ctx.settings.YnoDoubleBindings.value) - checkSymAssign(denot.symbol) - - // additional checks that intercept `denot` can be added here - - lastDenotation = denot - checkDenot() - lastSymbol = denot.symbol - checkedPeriod = Nowhere - } - - private[dotc] def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType = - if (sig != signature) - withSig(signature).withSym(sym, signature).asInstanceOf[ThisType] - else { - setSym(sym) - this - } - - private[dotc] final def setSym(sym: Symbol)(implicit ctx: Context): Unit = { - if (Config.checkNoDoubleBindings) - if (ctx.settings.YnoDoubleBindings.value) - checkSymAssign(sym) - uncheckedSetSym(sym) - } - - private[dotc] final def uncheckedSetSym(sym: Symbol): Unit = { - lastDenotation = null - lastSymbol = sym - checkedPeriod = Nowhere - } - - private def withSig(sig: Signature)(implicit ctx: Context): NamedType = - TermRef.withSig(prefix, name.asTermName, sig) - - protected def loadDenot(implicit ctx: Context): Denotation = { - val d = asMemberOf(prefix) - if (d.exists || ctx.phaseId == FirstPhaseId || !lastDenotation.isInstanceOf[SymDenotation]) - d - else { // name has changed; try load in earlier phase and make current - val d = loadDenot(ctx.withPhase(ctx.phaseId - 1)).current - if (d.exists) d - else throw new Error(s"failure to reload $this of class $getClass") - } - } - - protected def asMemberOf(prefix: Type)(implicit ctx: Context): Denotation = - if (name.isShadowedName) prefix.nonPrivateMember(name.revertShadowed) - else prefix.member(name) - - - /** (1) Reduce a type-ref `W # X` or `W { ... } # U`, where `W` is a wildcard type - * to an (unbounded) wildcard type. - * - * (2) Reduce a type-ref `T { X = U; ... } # X` to `U` - * provided `U` does not refer with a RecThis to the - * refinement type `T { X = U; ... }` - */ - def reduceProjection(implicit ctx: Context): Type = { - val reduced = prefix.lookupRefined(name) - if (reduced.exists) reduced else this - } - - def symbol(implicit ctx: Context): Symbol = { - val now = ctx.period - if (checkedPeriod == now || - lastDenotation == null && lastSymbol != null) lastSymbol - else denot.symbol - } - - /** Retrieves currently valid symbol without necessarily updating denotation. - * Assumes that symbols do not change between periods in the same run. - * Used to get the class underlying a ThisType. - */ - private[Types] def stableInRunSymbol(implicit ctx: Context): Symbol = - if (checkedPeriod.runId == ctx.runId) lastSymbol - else symbol - - def info(implicit ctx: Context): Type = denot.info - - def isType = isInstanceOf[TypeRef] - def isTerm = isInstanceOf[TermRef] - - /** Guard against cycles that can arise if given `op` - * follows info. The problematic cases are a type alias to itself or - * bounded by itself or a val typed as itself: - * - * type T <: T - * val x: x.type - * - * These are errors but we have to make sure that operations do - * not loop before the error is detected. - */ - final def controlled[T](op: => T)(implicit ctx: Context): T = try { - ctx.underlyingRecursions += 1 - if (ctx.underlyingRecursions < Config.LogPendingUnderlyingThreshold) - op - else if (ctx.pendingUnderlying contains this) - throw CyclicReference(symbol) - else - try { - ctx.pendingUnderlying += this - op - } finally { - ctx.pendingUnderlying -= this - } - } finally { - ctx.underlyingRecursions -= 1 - } - - /** A selection of the same kind, but with potentially a different prefix. - * The following normalizations are performed for type selections T#A: - * - * T#A --> B if A is bound to an alias `= B` in T - * - * If Config.splitProjections is set: - * - * (S & T)#A --> S#A if T does not have a member named A - * --> T#A if S does not have a member named A - * --> S#A & T#A otherwise - * (S | T)#A --> S#A | T#A - */ - def derivedSelect(prefix: Type)(implicit ctx: Context): Type = - if (prefix eq this.prefix) this - else if (isType) { - val res = prefix.lookupRefined(name) - if (res.exists) res - else if (Config.splitProjections) - prefix match { - case prefix: AndType => - def isMissing(tp: Type) = tp match { - case tp: TypeRef => !tp.info.exists - case _ => false - } - val derived1 = derivedSelect(prefix.tp1) - val derived2 = derivedSelect(prefix.tp2) - return ( - if (isMissing(derived1)) derived2 - else if (isMissing(derived2)) derived1 - else prefix.derivedAndType(derived1, derived2)) - case prefix: OrType => - val derived1 = derivedSelect(prefix.tp1) - val derived2 = derivedSelect(prefix.tp2) - return prefix.derivedOrType(derived1, derived2) - case _ => - newLikeThis(prefix) - } - else newLikeThis(prefix) - } - else newLikeThis(prefix) - - /** Create a NamedType of the same kind as this type, but with a new prefix. - */ - def newLikeThis(prefix: Type)(implicit ctx: Context): NamedType = - NamedType(prefix, name) - - /** Create a NamedType of the same kind as this type, but with a "inherited name". - * This is necessary to in situations like the following: - * - * class B { def m: T1 } - * class C extends B { private def m: T2; ... C.m } - * object C extends C - * object X { ... C.m } - * - * The two references of C.m in class C and object X refer to different - * definitions: The one in C refers to C#m whereas the one in X refers to B#m. - * But the type C.m must have only one denotation, so it can't refer to two - * members depending on context. - * - * In situations like this, the reference in X would get the type - * `<C.m>.shadowed` to make clear that we mean the inherited member, not - * the private one. - * - * Note: An alternative, possibly more robust scheme would be to give - * private members special names. A private definition would have a special - * name (say m' in the example above), but would be entered in its enclosing - * under both private and public names, so it could still be found by looking up - * the public name. - */ - final def shadowed(implicit ctx: Context): NamedType = - NamedType(prefix, name.shadowedName) - - override def equals(that: Any) = that match { - case that: NamedType => - this.name == that.name && - this.prefix == that.prefix && - !that.isInstanceOf[TermRefWithSignature] && - !that.isInstanceOf[WithFixedSym] - case _ => - false - } - - /* A version of toString which also prints aliases. Can be used for debugging - override def toString = - if (isTerm) s"TermRef($prefix, $name)" - else s"TypeRef($prefix, $name)${ - if (lastDenotation != null && lastDenotation.infoOrCompleter.isAlias) - s"@@@ ${lastDenotation.infoOrCompleter.asInstanceOf[TypeAlias].hi}" - else ""}" - */ - } - - abstract case class TermRef(override val prefix: Type, name: TermName) extends NamedType with SingletonType { - - type ThisType = TermRef - - //assert(name.toString != "<local Coder>") - override def underlying(implicit ctx: Context): Type = { - val d = denot - if (d.isOverloaded) NoType else d.info - } - - override def signature(implicit ctx: Context): Signature = denot.signature - - override def isOverloaded(implicit ctx: Context) = denot.isOverloaded - - private def rewrap(sd: SingleDenotation)(implicit ctx: Context) = - TermRef.withSigAndDenot(prefix, name, sd.signature, sd) - - def alternatives(implicit ctx: Context): List[TermRef] = - denot.alternatives map rewrap - - def altsWith(p: Symbol => Boolean)(implicit ctx: Context): List[TermRef] = - denot.altsWith(p) map rewrap - } - - abstract case class TypeRef(override val prefix: Type, name: TypeName) extends NamedType { - - type ThisType = TypeRef - - override def underlying(implicit ctx: Context): Type = info - - override def superType(implicit ctx: Context): Type = info match { - case TypeBounds(_, hi) => hi - case _ => info - } - } - - final class TermRefWithSignature(prefix: Type, name: TermName, override val sig: Signature) extends TermRef(prefix, name) { - assert(prefix ne NoPrefix) - override def signature(implicit ctx: Context) = sig - override def loadDenot(implicit ctx: Context): Denotation = { - val d = super.loadDenot - if (sig eq Signature.OverloadedSignature) d - else d.atSignature(sig).checkUnique - } - - override def newLikeThis(prefix: Type)(implicit ctx: Context): TermRef = { - val candidate = TermRef.withSig(prefix, name, sig) - if (symbol.exists && !candidate.symbol.exists) { // recompute from previous symbol - val ownSym = symbol - val newd = asMemberOf(prefix) - candidate.withDenot(newd.suchThat(_.signature == ownSym.signature)) - } - else candidate - } - - override def equals(that: Any) = that match { - case that: TermRefWithSignature => - this.prefix == that.prefix && - this.name == that.name && - this.sig == that.sig - case _ => - false - } - override def computeHash = doHash((name, sig), prefix) - override def toString = super.toString ++ s"/withSig($sig)" - } - - trait WithFixedSym extends NamedType { - def fixedSym: Symbol - assert(fixedSym ne NoSymbol) - uncheckedSetSym(fixedSym) - - override def withDenot(denot: Denotation)(implicit ctx: Context): ThisType = { - assert(denot.symbol eq fixedSym) - setDenot(denot) - this - } - - override def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType = - unsupported("withSym") - - override def newLikeThis(prefix: Type)(implicit ctx: Context): NamedType = - NamedType.withFixedSym(prefix, fixedSym) - - override def equals(that: Any) = that match { - case that: WithFixedSym => this.prefix == that.prefix && (this.fixedSym eq that.fixedSym) - case _ => false - } - override def computeHash = doHash(fixedSym, prefix) - } - - final class CachedTermRef(prefix: Type, name: TermName, hc: Int) extends TermRef(prefix, name) { - assert(prefix ne NoPrefix) - myHash = hc - override def computeHash = unsupported("computeHash") - } - - final class CachedTypeRef(prefix: Type, name: TypeName, hc: Int) extends TypeRef(prefix, name) { - assert(prefix ne NoPrefix) - myHash = hc - override def computeHash = unsupported("computeHash") - } - - // Those classes are non final as Linker extends them. - class TermRefWithFixedSym(prefix: Type, name: TermName, val fixedSym: TermSymbol) extends TermRef(prefix, name) with WithFixedSym - class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithFixedSym - - /** Assert current phase does not have erasure semantics */ - private def assertUnerased()(implicit ctx: Context) = - if (Config.checkUnerased) assert(!ctx.phase.erasedTypes) - - object NamedType { - def apply(prefix: Type, name: Name)(implicit ctx: Context) = - if (name.isTermName) TermRef.all(prefix, name.asTermName) - else TypeRef(prefix, name.asTypeName) - def apply(prefix: Type, name: Name, denot: Denotation)(implicit ctx: Context) = - if (name.isTermName) TermRef(prefix, name.asTermName, denot) - else TypeRef(prefix, name.asTypeName, denot) - def withFixedSym(prefix: Type, sym: Symbol)(implicit ctx: Context) = - if (sym.isType) TypeRef.withFixedSym(prefix, sym.name.asTypeName, sym.asType) - else TermRef.withFixedSym(prefix, sym.name.asTermName, sym.asTerm) - def withSymAndName(prefix: Type, sym: Symbol, name: Name)(implicit ctx: Context): NamedType = - if (sym.isType) TypeRef.withSymAndName(prefix, sym.asType, name.asTypeName) - else TermRef.withSymAndName(prefix, sym.asTerm, name.asTermName) - } - - object TermRef { - - private def symbolicRefs(implicit ctx: Context) = ctx.phase.symbolicRefs - - /** Create term ref with given name, without specifying a signature. - * Its meaning is the (potentially multi-) denotation of the member(s) - * of prefix with given name. - */ - def all(prefix: Type, name: TermName)(implicit ctx: Context): TermRef = { - ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TermRef] - } - - /** Create term ref referring to given symbol, taking the signature - * from the symbol if it is completed, or creating a term ref without - * signature, if symbol is not yet completed. - */ - def apply(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef = - withSymAndName(prefix, sym, sym.name) - - /** Create term ref to given initial denotation, taking the signature - * from the denotation if it is completed, or creating a term ref without - * signature, if denotation is not yet completed. - */ - def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef = { - if ((prefix eq NoPrefix) || denot.symbol.isFresh || symbolicRefs) - apply(prefix, denot.symbol.asTerm) - else denot match { - case denot: SymDenotation if denot.isCompleted => withSig(prefix, name, denot.signature) - case _ => all(prefix, name) - } - } withDenot denot - - /** Create a non-member term ref (which cannot be reloaded using `member`), - * with given prefix, name, and signature - */ - def withFixedSym(prefix: Type, name: TermName, sym: TermSymbol)(implicit ctx: Context): TermRef = - unique(new TermRefWithFixedSym(prefix, name, sym)) - - /** Create a term ref referring to given symbol with given name, taking the signature - * from the symbol if it is completed, or creating a term ref without - * signature, if symbol is not yet completed. This is very similar to TermRef(Type, Symbol), - * except for two differences: - * (1) The symbol might not yet have a denotation, so the name needs to be given explicitly. - * (2) The name in the term ref need not be the same as the name of the Symbol. - */ - def withSymAndName(prefix: Type, sym: TermSymbol, name: TermName)(implicit ctx: Context): TermRef = - if ((prefix eq NoPrefix) || sym.isFresh || symbolicRefs) - withFixedSym(prefix, name, sym) - else if (sym.defRunId != NoRunId && sym.isCompleted) - withSig(prefix, name, sym.signature) withSym (sym, sym.signature) - // Linker note: - // this is problematic, as withSig method could return a hash-consed refference - // that could have symbol already set making withSym trigger a double-binding error - // ./tests/run/absoverride.scala demonstates this - else - all(prefix, name) withSym (sym, Signature.NotAMethod) - - /** Create a term ref to given symbol, taking the signature from the symbol - * (which must be completed). - */ - def withSig(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef = - if ((prefix eq NoPrefix) || sym.isFresh || symbolicRefs) withFixedSym(prefix, sym.name, sym) - else withSig(prefix, sym.name, sym.signature).withSym(sym, sym.signature) - - /** Create a term ref with given prefix, name and signature */ - def withSig(prefix: Type, name: TermName, sig: Signature)(implicit ctx: Context): TermRef = - unique(new TermRefWithSignature(prefix, name, sig)) - - /** Create a term ref with given prefix, name, signature, and initial denotation */ - def withSigAndDenot(prefix: Type, name: TermName, sig: Signature, denot: Denotation)(implicit ctx: Context): TermRef = { - if ((prefix eq NoPrefix) || denot.symbol.isFresh || symbolicRefs) - withFixedSym(prefix, denot.symbol.asTerm.name, denot.symbol.asTerm) - else - withSig(prefix, name, sig) - } withDenot denot - } - - object TypeRef { - /** Create type ref with given prefix and name */ - def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef = - ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TypeRef] - - /** Create type ref to given symbol */ - def apply(prefix: Type, sym: TypeSymbol)(implicit ctx: Context): TypeRef = - withSymAndName(prefix, sym, sym.name) - - /** Create a non-member type ref (which cannot be reloaded using `member`), - * with given prefix, name, and symbol. - */ - def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef = - unique(new TypeRefWithFixedSym(prefix, name, sym)) - - /** Create a type ref referring to given symbol with given name. - * This is very similar to TypeRef(Type, Symbol), - * except for two differences: - * (1) The symbol might not yet have a denotation, so the name needs to be given explicitly. - * (2) The name in the type ref need not be the same as the name of the Symbol. - */ - def withSymAndName(prefix: Type, sym: TypeSymbol, name: TypeName)(implicit ctx: Context): TypeRef = - if ((prefix eq NoPrefix) || sym.isFresh) withFixedSym(prefix, name, sym) - else apply(prefix, name).withSym(sym, Signature.NotAMethod) - - /** Create a type ref with given name and initial denotation */ - def apply(prefix: Type, name: TypeName, denot: Denotation)(implicit ctx: Context): TypeRef = { - if ((prefix eq NoPrefix) || denot.symbol.isFresh) apply(prefix, denot.symbol.asType) - else apply(prefix, name) - } withDenot denot - } - - // --- Other SingletonTypes: ThisType/SuperType/ConstantType --------------------------- - - /** The type cls.this - * @param tref A type ref which indicates the class `cls`. - * Note: we do not pass a class symbol directly, because symbols - * do not survive runs whereas typerefs do. - */ - abstract case class ThisType(tref: TypeRef) extends CachedProxyType with SingletonType { - def cls(implicit ctx: Context): ClassSymbol = tref.stableInRunSymbol.asClass - override def underlying(implicit ctx: Context): Type = - if (ctx.erasedTypes) tref else cls.classInfo.selfType - override def computeHash = doHash(tref) - } - - final class CachedThisType(tref: TypeRef) extends ThisType(tref) - - object ThisType { - /** Normally one should use ClassSymbol#thisType instead */ - def raw(tref: TypeRef)(implicit ctx: Context) = - unique(new CachedThisType(tref)) - } - - /** The type of a super reference cls.super where - * `thistpe` is cls.this and `supertpe` is the type of the value referenced - * by `super`. - */ - abstract case class SuperType(thistpe: Type, supertpe: Type) extends CachedProxyType with SingletonType { - override def underlying(implicit ctx: Context) = supertpe - def derivedSuperType(thistpe: Type, supertpe: Type)(implicit ctx: Context) = - if ((thistpe eq this.thistpe) && (supertpe eq this.supertpe)) this - else SuperType(thistpe, supertpe) - override def computeHash = doHash(thistpe, supertpe) - } - - final class CachedSuperType(thistpe: Type, supertpe: Type) extends SuperType(thistpe, supertpe) - - object SuperType { - def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = { - assert(thistpe != NoPrefix) - unique(new CachedSuperType(thistpe, supertpe)) - } - } - - /** A constant type with single `value`. */ - abstract case class ConstantType(value: Constant) extends CachedProxyType with SingletonType { - override def underlying(implicit ctx: Context) = value.tpe - override def computeHash = doHash(value) - } - - final class CachedConstantType(value: Constant) extends ConstantType(value) - - object ConstantType { - def apply(value: Constant)(implicit ctx: Context) = { - assertUnerased() - unique(new CachedConstantType(value)) - } - } - - case class LazyRef(refFn: () => Type) extends UncachedProxyType with ValueType { - private var myRef: Type = null - private var computed = false - def ref = { - if (computed) assert(myRef != null) - else { - computed = true - myRef = refFn() - } - myRef - } - def evaluating = computed && myRef == null - override def underlying(implicit ctx: Context) = ref - override def toString = s"LazyRef($ref)" - override def equals(other: Any) = other match { - case other: LazyRef => this.ref.equals(other.ref) - case _ => false - } - override def hashCode = ref.hashCode + 37 - } - - // --- Refined Type and RecType ------------------------------------------------ - - abstract class RefinedOrRecType extends CachedProxyType with ValueType { - def parent: Type - } - - /** A refined type parent { refinement } - * @param refinedName The name of the refinement declaration - * @param infoFn: A function that produces the info of the refinement declaration, - * given the refined type itself. - */ - abstract case class RefinedType(parent: Type, refinedName: Name, refinedInfo: Type) extends RefinedOrRecType { - - override def underlying(implicit ctx: Context) = parent - - private def badInst = - throw new AssertionError(s"bad instantiation: $this") - - def checkInst(implicit ctx: Context): this.type = this // debug hook - - def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(implicit ctx: Context): Type = - if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this - else RefinedType(parent, refinedName, refinedInfo) - - /** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */ - def wrapIfMember(parent: Type)(implicit ctx: Context): Type = - if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo) - else parent - - override def equals(that: Any) = that match { - case that: RefinedType => - this.parent == that.parent && - this.refinedName == that.refinedName && - this.refinedInfo == that.refinedInfo - case _ => - false - } - override def computeHash = doHash(refinedName, refinedInfo, parent) - override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)" - } - - class CachedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type, hc: Int) - extends RefinedType(parent, refinedName, refinedInfo) { - myHash = hc - override def computeHash = unsupported("computeHash") - } - - object RefinedType { - def make(parent: Type, names: List[Name], infos: List[Type])(implicit ctx: Context): Type = - if (names.isEmpty) parent - else make(RefinedType(parent, names.head, infos.head), names.tail, infos.tail) - - def apply(parent: Type, name: Name, info: Type)(implicit ctx: Context): RefinedType = { - assert(!ctx.erasedTypes) - ctx.base.uniqueRefinedTypes.enterIfNew(parent, name, info).checkInst - } - } - - class RecType(parentExp: RecType => Type) extends RefinedOrRecType with BindingType { - - // See discussion in findMember#goRec why these vars are needed - private[Types] var opened: Boolean = false - private[Types] var openedTwice: Boolean = false - - val parent = parentExp(this) - - override def underlying(implicit ctx: Context): Type = parent - - def derivedRecType(parent: Type)(implicit ctx: Context): RecType = - if (parent eq this.parent) this - else RecType(rt => parent.substRecThis(this, RecThis(rt))) - - def rebind(parent: Type)(implicit ctx: Context): Type = - if (parent eq this.parent) this - else RecType.closeOver(rt => parent.substRecThis(this, RecThis(rt))) - - override def equals(other: Any) = other match { - case other: RecType => other.parent == this.parent - case _ => false - } - - def isReferredToBy(tp: Type)(implicit ctx: Context): Boolean = { - val refacc = new TypeAccumulator[Boolean] { - override def apply(x: Boolean, tp: Type) = x || { - tp match { - case tp: TypeRef => apply(x, tp.prefix) - case tp: RecThis => RecType.this eq tp.binder - case tp: LazyRef => true // To be safe, assume a reference exists - case _ => foldOver(x, tp) - } - } - } - refacc.apply(false, tp) - } - - override def computeHash = doHash(parent) - override def toString = s"RecType($parent | $hashCode)" - - private def checkInst(implicit ctx: Context): this.type = this // debug hook - } - - object RecType { - - /** Create a RecType, normalizing its contents. This means: - * - * 1. Nested Rec types on the type's spine are merged with the outer one. - * 2. Any refinement of the form `type T = z.T` on the spine of the type - * where `z` refers to the created rec-type is replaced by - * `type T`. This avoids infinite recursons later when we - * try to follow these references. - * TODO: Figure out how to guarantee absence of cycles - * of length > 1 - */ - def apply(parentExp: RecType => Type)(implicit ctx: Context): RecType = { - val rt = new RecType(parentExp) - def normalize(tp: Type): Type = tp.stripTypeVar match { - case tp: RecType => - normalize(tp.parent.substRecThis(tp, RecThis(rt))) - case tp @ RefinedType(parent, rname, rinfo) => - val rinfo1 = rinfo match { - case TypeAlias(TypeRef(RecThis(`rt`), `rname`)) => TypeBounds.empty - case _ => rinfo - } - tp.derivedRefinedType(normalize(parent), rname, rinfo1) - case tp => - tp - } - unique(rt.derivedRecType(normalize(rt.parent))).checkInst - } - def closeOver(parentExp: RecType => Type)(implicit ctx: Context) = { - val rt = this(parentExp) - if (rt.isReferredToBy(rt.parent)) rt else rt.parent - } - } - - // --- AndType/OrType --------------------------------------------------------------- - - trait AndOrType extends ValueType { // todo: check where we can simplify using AndOrType - def tp1: Type - def tp2: Type - def isAnd: Boolean - def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type // needed? - } - - abstract case class AndType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType { - - def isAnd = true - - def derivedAndType(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this - else AndType.make(tp1, tp2) - - def derived_& (tp1: Type, tp2: Type)(implicit ctx: Context): Type = - if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this - else tp1 & tp2 - - def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - derivedAndType(tp1, tp2) - - override def computeHash = doHash(tp1, tp2) - } - - final class CachedAndType(tp1: Type, tp2: Type) extends AndType(tp1, tp2) - - object AndType { - def apply(tp1: Type, tp2: Type)(implicit ctx: Context) = { - assert(tp1.isValueType && tp2.isValueType, i"$tp1 & $tp2 / " + s"$tp1 & $tp2") - unchecked(tp1, tp2) - } - def unchecked(tp1: Type, tp2: Type)(implicit ctx: Context) = { - assertUnerased() - unique(new CachedAndType(tp1, tp2)) - } - def make(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - if ((tp1 eq tp2) || (tp2 eq defn.AnyType)) - tp1 - else if (tp1 eq defn.AnyType) - tp2 - else - apply(tp1, tp2) - } - - abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType { - - assert(tp1.isInstanceOf[ValueType] && tp2.isInstanceOf[ValueType]) - def isAnd = false - - private[this] var myJoin: Type = _ - private[this] var myJoinPeriod: Period = Nowhere - - /** Replace or type by the closest non-or type above it */ - def join(implicit ctx: Context): Type = { - if (myJoinPeriod != ctx.period) { - myJoin = ctx.orDominator(this) - core.println(i"join of $this == $myJoin") - assert(myJoin != this) - myJoinPeriod = ctx.period - } - myJoin - } - - def derivedOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this - else OrType.make(tp1, tp2) - - def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - derivedOrType(tp1, tp2) - - override def computeHash = doHash(tp1, tp2) - } - - final class CachedOrType(tp1: Type, tp2: Type) extends OrType(tp1, tp2) - - object OrType { - def apply(tp1: Type, tp2: Type)(implicit ctx: Context) = { - assertUnerased() - unique(new CachedOrType(tp1, tp2)) - } - def make(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - if (tp1 eq tp2) tp1 else apply(tp1, tp2) - } - - // ----- Method types: MethodType/ExprType/PolyType ------------------------------- - - // Note: method types are cached whereas poly types are not. The reason - // is that most poly types are cyclic via poly params, - // and therefore two different poly types would never be equal. - - /** A trait that mixes in functionality for signature caching */ - trait MethodicType extends TermType { - - private[this] var mySignature: Signature = _ - private[this] var mySignatureRunId: Int = NoRunId - - protected def computeSignature(implicit ctx: Context): Signature - - protected def resultSignature(implicit ctx: Context) = try resultType match { - case rtp: MethodicType => rtp.signature - case tp => Signature(tp, isJava = false) - } - catch { - case ex: AssertionError => - println(i"failure while taking result signture of $this: $resultType") - throw ex - } - - final override def signature(implicit ctx: Context): Signature = { - if (ctx.runId != mySignatureRunId) { - mySignature = computeSignature - if (!mySignature.isUnderDefined) mySignatureRunId = ctx.runId - } - mySignature - } - } - - trait MethodOrPoly extends MethodicType - - abstract case class MethodType(paramNames: List[TermName], paramTypes: List[Type]) - (resultTypeExp: MethodType => Type) - extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType => - import MethodType._ - - def isJava = false - def isImplicit = false - - private[core] val resType = resultTypeExp(this) - assert(resType.exists) - - override def resultType(implicit ctx: Context): Type = - if (dependencyStatus == FalseDeps) { // dealias all false dependencies - val dealiasMap = new TypeMap { - def apply(tp: Type) = tp match { - case tp @ TypeRef(pre, name) => - tp.info match { - case TypeAlias(alias) if depStatus(pre) == TrueDeps => apply(alias) - case _ => mapOver(tp) - } - case _ => - mapOver(tp) - } - } - dealiasMap(resType) - } - else resType - - var myDependencyStatus: DependencyStatus = Unknown - - private def depStatus(tp: Type)(implicit ctx: Context): DependencyStatus = { - def combine(x: DependencyStatus, y: DependencyStatus) = { - val status = (x & StatusMask) max (y & StatusMask) - val provisional = (x | y) & Provisional - (if (status == TrueDeps) status else status | provisional).toByte - } - val depStatusAcc = new TypeAccumulator[DependencyStatus] { - def apply(status: DependencyStatus, tp: Type) = - if (status == TrueDeps) status - else - tp match { - case MethodParam(`thisMethodType`, _) => TrueDeps - case tp: TypeRef => - val status1 = foldOver(status, tp) - tp.info match { // follow type alias to avoid dependency - case TypeAlias(alias) if status1 == TrueDeps && status != TrueDeps => - combine(apply(status, alias), FalseDeps) - case _ => - status1 - } - case tp: TypeVar if !tp.isInstantiated => combine(status, Provisional) - case _ => foldOver(status, tp) - } - } - depStatusAcc(NoDeps, tp) - } - - /** The dependency status of this method. Some examples: - * - * class C extends { type S; type T = String } - * def f(x: C)(y: Boolean) // dependencyStatus = NoDeps - * def f(x: C)(y: x.S) // dependencyStatus = TrueDeps - * def f(x: C)(y: x.T) // dependencyStatus = FalseDeps, i.e. - * // dependency can be eliminated by dealiasing. - */ - private def dependencyStatus(implicit ctx: Context): DependencyStatus = { - if (myDependencyStatus != Unknown) myDependencyStatus - else { - val result = depStatus(resType) - if ((result & Provisional) == 0) myDependencyStatus = result - (result & StatusMask).toByte - } - } - - /** Does result type contain references to parameters of this method type, - * which cannot be eliminated by de-aliasing? - */ - def isDependent(implicit ctx: Context): Boolean = dependencyStatus == TrueDeps - - protected def computeSignature(implicit ctx: Context): Signature = - resultSignature.prepend(paramTypes, isJava) - - def derivedMethodType(paramNames: List[TermName], paramTypes: List[Type], resType: Type)(implicit ctx: Context) = - if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (resType eq this.resType)) this - else { - val resTypeFn = (x: MethodType) => resType.subst(this, x) - if (isJava) JavaMethodType(paramNames, paramTypes)(resTypeFn) - else if (isImplicit) ImplicitMethodType(paramNames, paramTypes)(resTypeFn) - else MethodType(paramNames, paramTypes)(resTypeFn) - } - - def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type = - if (isDependent) resultType.substParams(this, argTypes) - else resultType - - override def equals(that: Any) = that match { - case that: MethodType => - this.paramNames == that.paramNames && - this.paramTypes == that.paramTypes && - this.resType == that.resType - case _ => - false - } - - override def computeHash = doHash(paramNames, resType, paramTypes) - - protected def prefixString = "MethodType" - override def toString = s"$prefixString($paramNames, $paramTypes, $resType)" - } - - final class CachedMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type) - extends MethodType(paramNames, paramTypes)(resultTypeExp) { - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[CachedMethodType] - } - - final class JavaMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type) - extends MethodType(paramNames, paramTypes)(resultTypeExp) { - override def isJava = true - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[JavaMethodType] - override def computeHash = addDelta(super.computeHash, 1) - override protected def prefixString = "JavaMethodType" - } - - final class ImplicitMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type) - extends MethodType(paramNames, paramTypes)(resultTypeExp) { - override def isImplicit = true - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[ImplicitMethodType] - override def computeHash = addDelta(super.computeHash, 2) - override protected def prefixString = "ImplicitMethodType" - } - - abstract class MethodTypeCompanion { - def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType - def apply(paramNames: List[TermName], paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = - apply(paramNames, paramTypes)(_ => resultType) - def apply(paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - apply(nme.syntheticParamNames(paramTypes.length), paramTypes)(resultTypeExp) - def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = - apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType) - - /** Produce method type from parameter symbols, with special mappings for repeated - * and inline parameters. - */ - def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { - /** Replace @repeated annotations on Seq or Array types by <repeated> types */ - def translateRepeated(tp: Type): Type = tp match { - case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1)) - case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot => - val typeSym = tp.typeSymbol.asClass - assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass) - tp.translateParameterized(typeSym, defn.RepeatedParamClass) - case tp => - tp - } - /** Add @inlineParam to inline call-by-value parameters */ - def translateInline(tp: Type): Type = tp match { - case _: ExprType => tp - case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot)) - } - def paramInfo(param: Symbol): Type = { - val paramType = translateRepeated(param.info) - if (param.is(Inline)) translateInline(paramType) else paramType - } - def transformResult(mt: MethodType) = - resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _))) - apply(params map (_.name.asTermName), params map paramInfo)(transformResult _) - } - } - - object MethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) = - unique(new CachedMethodType(paramNames, paramTypes)(resultTypeExp)) - - private type DependencyStatus = Byte - private final val Unknown: DependencyStatus = 0 // not yet computed - private final val NoDeps: DependencyStatus = 1 // no dependent parameters found - private final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types - private final val TrueDeps: DependencyStatus = 3 // some truly dependent parameters exist - private final val StatusMask: DependencyStatus = 3 // the bits indicating actual dependency status - private final val Provisional: DependencyStatus = 4 // set if dependency status can still change due to type variable instantiations - } - - object JavaMethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) = - unique(new JavaMethodType(paramNames, paramTypes)(resultTypeExp)) - } - - object ImplicitMethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) = - unique(new ImplicitMethodType(paramNames, paramTypes)(resultTypeExp)) - } - - /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */ - abstract case class ExprType(resType: Type) - extends CachedProxyType with TermType with MethodicType { - override def resultType(implicit ctx: Context): Type = resType - override def underlying(implicit ctx: Context): Type = resType - protected def computeSignature(implicit ctx: Context): Signature = resultSignature - def derivedExprType(resType: Type)(implicit ctx: Context) = - if (resType eq this.resType) this else ExprType(resType) - override def computeHash = doHash(resType) - } - - final class CachedExprType(resultType: Type) extends ExprType(resultType) - - object ExprType { - def apply(resultType: Type)(implicit ctx: Context) = { - assertUnerased() - unique(new CachedExprType(resultType)) - } - } - - /** A type lambda of the form `[v_0 X_0, ..., v_n X_n] => T` */ - class PolyType(val paramNames: List[TypeName], val variances: List[Int])( - paramBoundsExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type) - extends CachedProxyType with BindingType with MethodOrPoly { - - /** The bounds of the type parameters */ - val paramBounds: List[TypeBounds] = paramBoundsExp(this) - - /** The result type of a PolyType / body of a type lambda */ - val resType: Type = resultTypeExp(this) - - assert(resType.isInstanceOf[TermType], this) - assert(paramNames.nonEmpty) - - protected def computeSignature(implicit ctx: Context) = resultSignature - - def isPolymorphicMethodType: Boolean = resType match { - case _: MethodType => true - case _ => false - } - - /** Is this polytype a higher-kinded type lambda as opposed to a polymorphic? - * method type? Only type lambdas get created with variances, that's how we can tell. - */ - def isTypeLambda: Boolean = variances.nonEmpty - - /** PolyParam references to all type parameters of this type */ - lazy val paramRefs: List[PolyParam] = paramNames.indices.toList.map(PolyParam(this, _)) - - lazy val typeParams: List[LambdaParam] = - paramNames.indices.toList.map(new LambdaParam(this, _)) - - override def resultType(implicit ctx: Context) = resType - override def underlying(implicit ctx: Context) = resType - - /** Instantiate result type by substituting parameters with given arguments */ - final def instantiate(argTypes: List[Type])(implicit ctx: Context): Type = - resultType.substParams(this, argTypes) - - /** Instantiate parameter bounds by substituting parameters with given arguments */ - final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[TypeBounds] = - paramBounds.mapConserve(_.substParams(this, argTypes).bounds) - - def newLikeThis(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): PolyType = - PolyType.apply(paramNames, variances)( - x => paramBounds mapConserve (_.subst(this, x).bounds), - x => resType.subst(this, x)) - - def derivedPolyType(paramNames: List[TypeName] = this.paramNames, - paramBounds: List[TypeBounds] = this.paramBounds, - resType: Type = this.resType)(implicit ctx: Context) = - if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (resType eq this.resType)) this - else newLikeThis(paramNames, paramBounds, resType) - - def derivedLambdaAbstraction(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): Type = - resType match { - case resType @ TypeAlias(alias) => - resType.derivedTypeAlias(newLikeThis(paramNames, paramBounds, alias)) - case resType @ TypeBounds(lo, hi) => - resType.derivedTypeBounds( - if (lo.isRef(defn.NothingClass)) lo else newLikeThis(paramNames, paramBounds, lo), - newLikeThis(paramNames, paramBounds, hi)) - case _ => - derivedPolyType(paramNames, paramBounds, resType) - } - - /** Merge nested polytypes into one polytype. nested polytypes are normally not supported - * but can arise as temporary data structures. - */ - def flatten(implicit ctx: Context): PolyType = resType match { - case that: PolyType => - val shift = new TypeMap { - def apply(t: Type) = t match { - case PolyParam(`that`, n) => PolyParam(that, n + paramNames.length) - case t => mapOver(t) - } - } - PolyType(paramNames ++ that.paramNames)( - x => this.paramBounds.mapConserve(_.subst(this, x).bounds) ++ - that.paramBounds.mapConserve(shift(_).subst(that, x).bounds), - x => shift(that.resultType).subst(that, x).subst(this, x)) - case _ => this - } - - /** The type `[tparams := paramRefs] tp`, where `tparams` can be - * either a list of type parameter symbols or a list of lambda parameters - */ - def lifted(tparams: List[TypeParamInfo], tp: Type)(implicit ctx: Context): Type = - tparams match { - case LambdaParam(poly, _) :: _ => tp.subst(poly, this) - case tparams: List[Symbol @unchecked] => tp.subst(tparams, paramRefs) - } - - override def equals(other: Any) = other match { - case other: PolyType => - other.paramNames == this.paramNames && - other.paramBounds == this.paramBounds && - other.resType == this.resType && - other.variances == this.variances - case _ => false - } - - override def toString = s"PolyType($variances, $paramNames, $paramBounds, $resType)" - - override def computeHash = doHash(variances ::: paramNames, resType, paramBounds) - } - - object PolyType { - def apply(paramNames: List[TypeName], variances: List[Int] = Nil)( - paramBoundsExp: PolyType => List[TypeBounds], - resultTypeExp: PolyType => Type)(implicit ctx: Context): PolyType = { - val vs = if (variances.isEmpty) paramNames.map(alwaysZero) else variances - unique(new PolyType(paramNames, vs)(paramBoundsExp, resultTypeExp)) - } - - def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] = - Some((tl.typeParams, tl.resType)) - - def any(n: Int)(implicit ctx: Context) = - apply(tpnme.syntheticTypeParamNames(n), List.fill(n)(0))( - pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType) - } - - // ----- HK types: LambdaParam, HKApply --------------------- - - /** The parameter of a type lambda */ - case class LambdaParam(tl: PolyType, n: Int) extends TypeParamInfo { - def isTypeParam(implicit ctx: Context) = true - def paramName(implicit ctx: Context): TypeName = tl.paramNames(n) - def paramBounds(implicit ctx: Context): TypeBounds = tl.paramBounds(n) - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds = paramBounds - def paramBoundsOrCompleter(implicit ctx: Context): Type = paramBounds - def paramVariance(implicit ctx: Context): Int = tl.variances(n) - def toArg: Type = PolyParam(tl, n) - def paramRef(implicit ctx: Context): Type = PolyParam(tl, n) - } - - /** A higher kinded type application `C[T_1, ..., T_n]` */ - abstract case class HKApply(tycon: Type, args: List[Type]) - extends CachedProxyType with ValueType { - - private var validSuper: Period = Nowhere - private var cachedSuper: Type = _ - - override def underlying(implicit ctx: Context): Type = tycon - - override def superType(implicit ctx: Context): Type = { - if (ctx.period != validSuper) { - cachedSuper = tycon match { - case tp: PolyType => defn.AnyType - case tp: TypeVar if !tp.inst.exists => - // supertype not stable, since underlying might change - return tp.underlying.applyIfParameterized(args) - case tp: TypeProxy => tp.superType.applyIfParameterized(args) - case _ => defn.AnyType - } - validSuper = ctx.period - } - cachedSuper - } - - def lowerBound(implicit ctx: Context) = tycon.stripTypeVar match { - case tycon: TypeRef => - tycon.info match { - case TypeBounds(lo, hi) => - if (lo eq hi) superType // optimization, can profit from caching in this case - else lo.applyIfParameterized(args) - case _ => NoType - } - case _ => - NoType - } - - def typeParams(implicit ctx: Context): List[TypeParamInfo] = { - val tparams = tycon.typeParams - if (tparams.isEmpty) PolyType.any(args.length).typeParams else tparams - } - - def derivedAppliedType(tycon: Type, args: List[Type])(implicit ctx: Context): Type = - if ((tycon eq this.tycon) && (args eq this.args)) this - else tycon.appliedTo(args) - - override def computeHash = doHash(tycon, args) - - protected def checkInst(implicit ctx: Context): this.type = { - def check(tycon: Type): Unit = tycon.stripTypeVar match { - case tycon: TypeRef if !tycon.symbol.isClass => - case _: PolyParam | ErrorType | _: WildcardType => - case _: PolyType => - assert(args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this") - case tycon: AnnotatedType => - check(tycon.underlying) - case _ => - assert(false, s"illegal type constructor in $this") - } - if (Config.checkHKApplications) check(tycon) - this - } - } - - final class CachedHKApply(tycon: Type, args: List[Type]) extends HKApply(tycon, args) - - object HKApply { - def apply(tycon: Type, args: List[Type])(implicit ctx: Context) = - unique(new CachedHKApply(tycon, args)).checkInst - } - - // ----- Bound types: MethodParam, PolyParam -------------------------- - - abstract class BoundType extends CachedProxyType with ValueType { - type BT <: Type - def binder: BT - // Dotty deviation: copyBoundType was copy, but - // dotty generates copy methods always automatically, and therefore - // does not accept same-named method definitions in subclasses. - // Scala2x, on the other hand, requires them (not sure why!) - def copyBoundType(bt: BT): Type - } - - abstract class ParamType extends BoundType { - def paramNum: Int - def paramName: Name - } - - abstract case class MethodParam(binder: MethodType, paramNum: Int) extends ParamType with SingletonType { - type BT = MethodType - - def paramName = binder.paramNames(paramNum) - - override def underlying(implicit ctx: Context): Type = binder.paramTypes(paramNum) - def copyBoundType(bt: BT) = new MethodParamImpl(bt, paramNum) - - // need to customize hashCode and equals to prevent infinite recursion for dep meth types. - override def computeHash = addDelta(binder.identityHash, paramNum) - override def equals(that: Any) = that match { - case that: MethodParam => - (this.binder eq that.binder) && this.paramNum == that.paramNum - case _ => - false - } - - override def toString = s"MethodParam($paramName)" - } - - class MethodParamImpl(binder: MethodType, paramNum: Int) extends MethodParam(binder, paramNum) - - object MethodParam { - def apply(binder: MethodType, paramNum: Int)(implicit ctx: Context): MethodParam = { - assertUnerased() - new MethodParamImpl(binder, paramNum) - } - } - - /** TODO Some docs would be nice here! */ - case class PolyParam(binder: PolyType, paramNum: Int) extends ParamType { - type BT = PolyType - def copyBoundType(bt: BT) = PolyParam(bt, paramNum) - - /** Looking only at the structure of `bound`, is one of the following true? - * - fromBelow and param <:< bound - * - !fromBelow and param >:> bound - */ - def occursIn(bound: Type, fromBelow: Boolean)(implicit ctx: Context): Boolean = bound.stripTypeVar match { - case bound: PolyParam => bound == this - case bound: AndOrType => - def occ1 = occursIn(bound.tp1, fromBelow) - def occ2 = occursIn(bound.tp2, fromBelow) - if (fromBelow == bound.isAnd) occ1 && occ2 else occ1 || occ2 - case _ => false - } - - def paramName = binder.paramNames(paramNum) - - override def underlying(implicit ctx: Context): Type = { - val bounds = binder.paramBounds - if (bounds == null) NoType // this can happen if the referenced generic type is not initialized yet - else bounds(paramNum) - } - // no customized hashCode/equals needed because cycle is broken in PolyType - override def toString = - try s"PolyParam($paramName)" - catch { - case ex: IndexOutOfBoundsException => s"PolyParam(<bad index: $paramNum>)" - } - - override def computeHash = doHash(paramNum, binder.identityHash) - - override def equals(that: Any) = that match { - case that: PolyParam => - (this.binder eq that.binder) && this.paramNum == that.paramNum - case _ => - false - } - } - - /** a self-reference to an enclosing recursive type. */ - case class RecThis(binder: RecType) extends BoundType with SingletonType { - type BT = RecType - override def underlying(implicit ctx: Context) = binder - def copyBoundType(bt: BT) = RecThis(bt) - - // need to customize hashCode and equals to prevent infinite recursion - // between RecTypes and RecRefs. - override def computeHash = addDelta(binder.identityHash, 41) - override def equals(that: Any) = that match { - case that: RecThis => this.binder eq that.binder - case _ => false - } - override def toString = - try s"RecThis(${binder.hashCode})" - catch { - case ex: NullPointerException => s"RecThis(<under construction>)" - } - } - - // ----- Skolem types ----------------------------------------------- - - /** A skolem type reference with underlying type `binder`. */ - abstract case class SkolemType(info: Type) extends UncachedProxyType with ValueType with SingletonType { - override def underlying(implicit ctx: Context) = info - def derivedSkolemType(info: Type)(implicit ctx: Context) = - if (info eq this.info) this else SkolemType(info) - override def hashCode: Int = identityHash - override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] - - private var myRepr: String = null - def repr(implicit ctx: Context) = { - if (myRepr == null) myRepr = ctx.freshName("?") - myRepr - } - - override def toString = s"Skolem($hashCode)" - } - - final class CachedSkolemType(info: Type) extends SkolemType(info) - - object SkolemType { - def apply(info: Type)(implicit ctx: Context) = - unique(new CachedSkolemType(info)) - } - - // ------------ Type variables ---------------------------------------- - - /** In a TypeApply tree, a TypeVar is created for each argument type to be inferred. - * Every type variable is referred to by exactly one inferred type parameter of some - * TypeApply tree. - * - * A type variable is essentially a switch that models some part of a substitution. - * It is first linked to `origin`, a poly param that's in the current constraint set. - * It can then be (once) instantiated to some other type. The instantiation is - * recorded in the type variable itself, or else, if the current type state - * is different from the variable's creation state (meaning unrolls are possible) - * in the current typer state. - * - * @param origin The parameter that's tracked by the type variable. - * @param creatorState The typer state in which the variable was created. - * @param owningTree The function part of the TypeApply tree tree that introduces - * the type variable. - * @paran owner The current owner if the context where the variable was created. - * - * `owningTree` and `owner` are used to determine whether a type-variable can be instantiated - * at some given point. See `Inferencing#interpolateUndetVars`. - */ - final class TypeVar(val origin: PolyParam, creatorState: TyperState, val owningTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType { - - /** The permanent instance type of the variable, or NoType is none is given yet */ - private[core] var inst: Type = NoType - - /** The state owning the variable. This is at first `creatorState`, but it can - * be changed to an enclosing state on a commit. - */ - private[core] var owningState = creatorState - - /** The instance type of this variable, or NoType if the variable is currently - * uninstantiated - */ - def instanceOpt(implicit ctx: Context): Type = - if (inst.exists) inst else { - ctx.typerState.ephemeral = true - ctx.typerState.instType(this) - } - - /** Is the variable already instantiated? */ - def isInstantiated(implicit ctx: Context) = instanceOpt.exists - - /** Instantiate variable with given type */ - private def instantiateWith(tp: Type)(implicit ctx: Context): Type = { - assert(tp ne this, s"self instantiation of ${tp.show}, constraint = ${ctx.typerState.constraint.show}") - typr.println(s"instantiating ${this.show} with ${tp.show}") - assert(ctx.typerState.constraint contains this) // !!! DEBUG - if ((ctx.typerState eq owningState) && !ctx.typeComparer.subtypeCheckInProgress) - inst = tp - ctx.typerState.constraint = ctx.typerState.constraint.replace(origin, tp) - tp - } - - /** Instantiate variable from the constraints over its `origin`. - * If `fromBelow` is true, the variable is instantiated to the lub - * of its lower bounds in the current constraint; otherwise it is - * instantiated to the glb of its upper bounds. However, a lower bound - * instantiation can be a singleton type only if the upper bound - * is also a singleton type. - */ - def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type = { - val inst = ctx.typeComparer.instanceType(origin, fromBelow) - if (ctx.typerState.isGlobalCommittable) - inst match { - case inst: PolyParam => - assert(inst.binder.isTypeLambda, i"bad inst $this := $inst, constr = ${ctx.typerState.constraint}") - // If this fails, you might want to turn on Config.debugCheckConstraintsClosed - // to help find the root of the problem. - // Note: Parameters of type lambdas are excluded from the assertion because - // they might arise from ill-kinded code. See #1652 - case _ => - } - instantiateWith(inst) - } - - /** Unwrap to instance (if instantiated) or origin (if not), until result - * is no longer a TypeVar - */ - override def stripTypeVar(implicit ctx: Context): Type = { - val inst = instanceOpt - if (inst.exists) inst.stripTypeVar else origin - } - - /** If the variable is instantiated, its instance, otherwise its origin */ - override def underlying(implicit ctx: Context): Type = { - val inst = instanceOpt - if (inst.exists) inst - else { - ctx.typerState.ephemeral = true - origin - } - } - - override def computeHash: Int = identityHash - override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] - - override def toString = { - def instStr = if (inst.exists) s" -> $inst" else "" - s"TypeVar($origin$instStr)" - } - } - - // ------ ClassInfo, Type Bounds ------------------------------------------------------------ - - /** Roughly: the info of a class during a period. - * @param prefix The prefix on which parents, decls, and selfType need to be rebased. - * @param cls The class symbol. - * @param classParents The parent types of this class. - * These are all normalized to be TypeRefs by moving any refinements - * to be member definitions of the class itself. - * @param decls The symbols defined directly in this class. - * @param selfInfo The type of `this` in this class, if explicitly given, - * NoType otherwise. If class is compiled from source, can also - * be a reference to the self symbol containing the type. - */ - abstract case class ClassInfo( - prefix: Type, - cls: ClassSymbol, - classParents: List[TypeRef], - decls: Scope, - selfInfo: DotClass /* should be: Type | Symbol */) extends CachedGroundType with TypeType { - - /** The self type of a class is the conjunction of - * - the explicit self type if given (or the info of a given self symbol), and - * - the fully applied reference to the class itself. - */ - def selfType(implicit ctx: Context): Type = { - if (selfTypeCache == null) - selfTypeCache = { - def fullRef = fullyAppliedRef - val given = givenSelfType - val raw = - if (!given.exists) fullRef - else if (cls is Module) given - else if (ctx.erasedTypes) fullRef - else AndType(given, fullRef) - raw//.asSeenFrom(prefix, cls.owner) - } - selfTypeCache - } - - /** The explicitly given self type (self types of modules are assumed to be - * explcitly given here). - */ - override def givenSelfType(implicit ctx: Context): Type = selfInfo match { - case tp: Type => tp - case self: Symbol => self.info - } - - private var selfTypeCache: Type = null - - private def fullyAppliedRef(base: Type, tparams: List[TypeSymbol])(implicit ctx: Context): Type = tparams match { - case tparam :: tparams1 => - fullyAppliedRef( - RefinedType(base, tparam.name, TypeRef(cls.thisType, tparam).toBounds(tparam)), - tparams1) - case nil => - base - } - - /** The class type with all type parameters */ - def fullyAppliedRef(implicit ctx: Context): Type = fullyAppliedRef(cls.typeRef, cls.typeParams) - - private var typeRefCache: TypeRef = null - - def typeRef(implicit ctx: Context): TypeRef = { - def clsDenot = if (prefix eq cls.owner.thisType) cls.denot else cls.denot.copySymDenotation(info = this) - if (typeRefCache == null) - typeRefCache = - if ((cls is PackageClass) || cls.owner.isTerm) symbolicTypeRef - else TypeRef(prefix, cls.name, clsDenot) - typeRefCache - } - - def symbolicTypeRef(implicit ctx: Context): TypeRef = TypeRef(prefix, cls) - - // cached because baseType needs parents - private var parentsCache: List[TypeRef] = null - - /** The parent type refs as seen from the given prefix */ - override def parents(implicit ctx: Context): List[TypeRef] = { - if (parentsCache == null) - parentsCache = cls.classParents.mapConserve(_.asSeenFrom(prefix, cls.owner).asInstanceOf[TypeRef]) - parentsCache - } - - /** The parent types with all type arguments */ - override def parentsWithArgs(implicit ctx: Context): List[Type] = - parents mapConserve { pref => - ((pref: Type) /: pref.classSymbol.typeParams) { (parent, tparam) => - val targSym = decls.lookup(tparam.name) - if (targSym.exists) RefinedType(parent, targSym.name, targSym.info) - else parent - } - } - - def derivedClassInfo(prefix: Type)(implicit ctx: Context) = - if (prefix eq this.prefix) this - else ClassInfo(prefix, cls, classParents, decls, selfInfo) - - def derivedClassInfo(prefix: Type = this.prefix, classParents: List[TypeRef] = classParents, decls: Scope = this.decls, selfInfo: DotClass = this.selfInfo)(implicit ctx: Context) = - if ((prefix eq this.prefix) && (classParents eq this.classParents) && (decls eq this.decls) && (selfInfo eq this.selfInfo)) this - else ClassInfo(prefix, cls, classParents, decls, selfInfo) - - override def computeHash = doHash(cls, prefix) - - override def toString = s"ClassInfo($prefix, $cls)" - } - - class CachedClassInfo(prefix: Type, cls: ClassSymbol, classParents: List[TypeRef], decls: Scope, selfInfo: DotClass) - extends ClassInfo(prefix, cls, classParents, decls, selfInfo) - - /** A class for temporary class infos where `parents` are not yet known. */ - final class TempClassInfo(prefix: Type, cls: ClassSymbol, decls: Scope, selfInfo: DotClass) - extends CachedClassInfo(prefix, cls, Nil, decls, selfInfo) { - - /** A list of actions that were because they rely on the class info of `cls` to - * be no longer temporary. These actions will be performed once `cls` gets a real - * ClassInfo. - */ - private var suspensions: List[() => Unit] = Nil - - def addSuspension(suspension: () => Unit): Unit = suspensions ::= suspension - - /** Install classinfo with known parents in `denot` and resume all suspensions */ - def finalize(denot: SymDenotation, parents: List[TypeRef])(implicit ctx: Context) = { - denot.info = derivedClassInfo(classParents = parents) - suspensions.foreach(_()) - } - } - - object ClassInfo { - def apply(prefix: Type, cls: ClassSymbol, classParents: List[TypeRef], decls: Scope, selfInfo: DotClass = NoType)(implicit ctx: Context) = - unique(new CachedClassInfo(prefix, cls, classParents, decls, selfInfo)) - } - - /** Type bounds >: lo <: hi */ - abstract case class TypeBounds(lo: Type, hi: Type) extends CachedProxyType with TypeType { - - assert(lo.isInstanceOf[TermType]) - assert(hi.isInstanceOf[TermType]) - - def variance: Int = 0 - - override def underlying(implicit ctx: Context): Type = hi - - /** The non-alias type bounds type with given bounds */ - def derivedTypeBounds(lo: Type, hi: Type)(implicit ctx: Context) = - if ((lo eq this.lo) && (hi eq this.hi) && (variance == 0)) this - else TypeBounds(lo, hi) - - /** If this is an alias, a derived alias with the new variance, - * Otherwise the type itself. - */ - def withVariance(variance: Int)(implicit ctx: Context) = this match { - case tp: TypeAlias => tp.derivedTypeAlias(tp.alias, variance) - case _ => this - } - - def contains(tp: Type)(implicit ctx: Context): Boolean = tp match { - case tp: TypeBounds => lo <:< tp.lo && tp.hi <:< hi - case tp: ClassInfo => - // Note: Taking a normal typeRef does not work here. A normal ref might contain - // also other information about the named type (e.g. bounds). - contains(tp.symbolicTypeRef) - case _ => lo <:< tp && tp <:< hi - } - - def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = - if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) that - else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) this - else TypeBounds(this.lo | that.lo, this.hi & that.hi) - - def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = - if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) this - else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) that - else TypeBounds(this.lo & that.lo, this.hi | that.hi) - - override def & (that: Type)(implicit ctx: Context) = that match { - case that: TypeBounds => this & that - case _ => super.& (that) - } - - override def | (that: Type)(implicit ctx: Context) = that match { - case that: TypeBounds => this | that - case _ => super.| (that) - } - - /** The implied bounds, where aliases are mapped to intervals from - * Nothing/Any - */ - def boundsInterval(implicit ctx: Context): TypeBounds = this - - /** If this type and that type have the same variance, this variance, otherwise 0 */ - final def commonVariance(that: TypeBounds): Int = (this.variance + that.variance) / 2 - - override def computeHash = doHash(variance, lo, hi) - override def equals(that: Any): Boolean = that match { - case that: TypeBounds => - (this.lo eq that.lo) && (this.hi eq that.hi) && (this.variance == that.variance) - case _ => - false - } - - override def toString = - if (lo eq hi) s"TypeAlias($lo, $variance)" else s"TypeBounds($lo, $hi)" - } - - class RealTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) - - abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias) { - /** pre: this is a type alias */ - def derivedTypeAlias(alias: Type, variance: Int = this.variance)(implicit ctx: Context) = - if ((alias eq this.alias) && (variance == this.variance)) this - else TypeAlias(alias, variance) - - override def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = { - val v = this commonVariance that - if (v > 0) derivedTypeAlias(this.hi & that.hi, v) - else if (v < 0) derivedTypeAlias(this.lo | that.lo, v) - else super.& (that) - } - - override def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = { - val v = this commonVariance that - if (v > 0) derivedTypeAlias(this.hi | that.hi, v) - else if (v < 0) derivedTypeAlias(this.lo & that.lo, v) - else super.| (that) - } - - override def boundsInterval(implicit ctx: Context): TypeBounds = - if (variance == 0) this - else if (variance < 0) TypeBounds.lower(alias) - else TypeBounds.upper(alias) - } - - class CachedTypeAlias(alias: Type, variance: Int, hc: Int) extends TypeAlias(alias, variance) { - myHash = hc - } - - object TypeBounds { - def apply(lo: Type, hi: Type)(implicit ctx: Context): TypeBounds = - unique(new RealTypeBounds(lo, hi)) - def empty(implicit ctx: Context) = apply(defn.NothingType, defn.AnyType) - def upper(hi: Type)(implicit ctx: Context) = apply(defn.NothingType, hi) - def lower(lo: Type)(implicit ctx: Context) = apply(lo, defn.AnyType) - } - - object TypeAlias { - def apply(alias: Type, variance: Int = 0)(implicit ctx: Context) = - ctx.uniqueTypeAliases.enterIfNew(alias, variance) - def unapply(tp: TypeAlias): Option[Type] = Some(tp.alias) - } - - // ----- Annotated and Import types ----------------------------------------------- - - /** An annotated type tpe @ annot */ - case class AnnotatedType(tpe: Type, annot: Annotation) - extends UncachedProxyType with ValueType { - // todo: cache them? but this makes only sense if annotations and trees are also cached. - override def underlying(implicit ctx: Context): Type = tpe - def derivedAnnotatedType(tpe: Type, annot: Annotation) = - if ((tpe eq this.tpe) && (annot eq this.annot)) this - else AnnotatedType(tpe, annot) - - override def stripTypeVar(implicit ctx: Context): Type = - derivedAnnotatedType(tpe.stripTypeVar, annot) - override def stripAnnots(implicit ctx: Context): Type = tpe.stripAnnots - } - - object AnnotatedType { - def make(underlying: Type, annots: List[Annotation]) = - (underlying /: annots)(AnnotatedType(_, _)) - } - - // Special type objects and classes ----------------------------------------------------- - - /** The type of an erased array */ - abstract case class JavaArrayType(elemType: Type) extends CachedGroundType with ValueType { - override def computeHash = doHash(elemType) - def derivedJavaArrayType(elemtp: Type)(implicit ctx: Context) = - if (elemtp eq this.elemType) this else JavaArrayType(elemtp) - } - final class CachedJavaArrayType(elemType: Type) extends JavaArrayType(elemType) - object JavaArrayType { - def apply(elemType: Type)(implicit ctx: Context) = unique(new CachedJavaArrayType(elemType)) - } - - /** The type of an import clause tree */ - case class ImportType(expr: Tree) extends UncachedGroundType - - /** Sentinel for "missing type" */ - @sharable case object NoType extends CachedGroundType { - override def exists = false - override def computeHash = hashSeed - } - - /** Missing prefix */ - @sharable case object NoPrefix extends CachedGroundType { - override def computeHash = hashSeed - } - - abstract class ErrorType extends UncachedGroundType with ValueType - - object ErrorType extends ErrorType - - /* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */ - object TryDynamicCallType extends ErrorType - - /** Wildcard type, possibly with bounds */ - abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType { - def derivedWildcardType(optBounds: Type)(implicit ctx: Context) = - if (optBounds eq this.optBounds) this - else if (!optBounds.exists) WildcardType - else WildcardType(optBounds.asInstanceOf[TypeBounds]) - override def computeHash = doHash(optBounds) - } - - final class CachedWildcardType(optBounds: Type) extends WildcardType(optBounds) - - @sharable object WildcardType extends WildcardType(NoType) { - def apply(bounds: TypeBounds)(implicit ctx: Context) = unique(new CachedWildcardType(bounds)) - } - - /** An extractor for single abstract method types. - * A type is a SAM type if it is a reference to a class or trait, which - * - * - has a single abstract method with a method type (ExprType - * and PolyType not allowed!) - * - can be instantiated without arguments or with just () as argument. - * - * The pattern `SAMType(denot)` matches a SAM type, where `denot` is the - * denotation of the single abstract method as a member of the type. - */ - object SAMType { - def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match { - case tp: ClassInfo => - def zeroParams(tp: Type): Boolean = tp match { - case pt: PolyType => zeroParams(pt.resultType) - case mt: MethodType => mt.paramTypes.isEmpty && !mt.resultType.isInstanceOf[MethodType] - case et: ExprType => true - case _ => false - } - if ((tp.cls is Trait) || zeroParams(tp.cls.primaryConstructor.info)) tp // !!! needs to be adapted once traits have parameters - else NoType - case tp: TypeRef => - zeroParamClass(tp.underlying) - case tp: RefinedType => - zeroParamClass(tp.underlying) - case tp: TypeBounds => - zeroParamClass(tp.underlying) - case tp: TypeVar => - zeroParamClass(tp.underlying) - case _ => - NoType - } - def isInstantiatable(tp: Type)(implicit ctx: Context): Boolean = zeroParamClass(tp) match { - case cinfo: ClassInfo => - val tref = tp.narrow - val selfType = cinfo.selfType.asSeenFrom(tref, cinfo.cls) - tref <:< selfType - case _ => - false - } - def unapply(tp: Type)(implicit ctx: Context): Option[SingleDenotation] = - if (isInstantiatable(tp)) { - val absMems = tp.abstractTermMembers - // println(s"absMems: ${absMems map (_.show) mkString ", "}") - if (absMems.size == 1) - absMems.head.info match { - case mt: MethodType if !mt.isDependent => Some(absMems.head) - case _ => None - } - else if (tp isRef defn.PartialFunctionClass) - // To maintain compatibility with 2.x, we treat PartialFunction specially, - // pretending it is a SAM type. In the future it would be better to merge - // Function and PartialFunction, have Function1 contain a isDefinedAt method - // def isDefinedAt(x: T) = true - // and overwrite that method whenever the function body is a sequence of - // case clauses. - absMems.find(_.symbol.name == nme.apply) - else None - } - else None - } - - // ----- TypeMaps -------------------------------------------------------------------- - - abstract class TypeMap(implicit protected val ctx: Context) extends (Type => Type) { thisMap => - - protected def stopAtStatic = true - - def apply(tp: Type): Type - - protected var variance = 1 - - protected def derivedSelect(tp: NamedType, pre: Type): Type = - tp.derivedSelect(pre) - protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type): Type = - tp.derivedRefinedType(parent, tp.refinedName, info) - protected def derivedRecType(tp: RecType, parent: Type): Type = - tp.rebind(parent) - protected def derivedTypeAlias(tp: TypeAlias, alias: Type): Type = - tp.derivedTypeAlias(alias) - protected def derivedTypeBounds(tp: TypeBounds, lo: Type, hi: Type): Type = - tp.derivedTypeBounds(lo, hi) - protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type): Type = - tp.derivedSuperType(thistp, supertp) - protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type = - tp.derivedAppliedType(tycon, args) - protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type): Type = - tp.derivedAndOrType(tp1, tp2) - protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation): Type = - tp.derivedAnnotatedType(underlying, annot) - protected def derivedWildcardType(tp: WildcardType, bounds: Type): Type = - tp.derivedWildcardType(bounds) - protected def derivedClassInfo(tp: ClassInfo, pre: Type): Type = - tp.derivedClassInfo(pre) - protected def derivedJavaArrayType(tp: JavaArrayType, elemtp: Type): Type = - tp.derivedJavaArrayType(elemtp) - protected def derivedMethodType(tp: MethodType, formals: List[Type], restpe: Type): Type = - tp.derivedMethodType(tp.paramNames, formals, restpe) - protected def derivedExprType(tp: ExprType, restpe: Type): Type = - tp.derivedExprType(restpe) - protected def derivedPolyType(tp: PolyType, pbounds: List[TypeBounds], restpe: Type): Type = - tp.derivedPolyType(tp.paramNames, pbounds, restpe) - - /** Map this function over given type */ - def mapOver(tp: Type): Type = { - implicit val ctx: Context = this.ctx // Dotty deviation: implicits need explicit type - tp match { - case tp: NamedType => - if (stopAtStatic && tp.symbol.isStatic) tp - else derivedSelect(tp, this(tp.prefix)) - - case _: ThisType - | _: BoundType - | NoPrefix => tp - - case tp: RefinedType => - derivedRefinedType(tp, this(tp.parent), this(tp.refinedInfo)) - - case tp: TypeAlias => - val saved = variance - variance = variance * tp.variance - val alias1 = this(tp.alias) - variance = saved - derivedTypeAlias(tp, alias1) - - case tp: TypeBounds => - variance = -variance - val lo1 = this(tp.lo) - variance = -variance - derivedTypeBounds(tp, lo1, this(tp.hi)) - - case tp: MethodType => - def mapOverMethod = { - variance = -variance - val ptypes1 = tp.paramTypes mapConserve this - variance = -variance - derivedMethodType(tp, ptypes1, this(tp.resultType)) - } - mapOverMethod - - case tp: ExprType => - derivedExprType(tp, this(tp.resultType)) - - case tp: PolyType => - def mapOverPoly = { - variance = -variance - val bounds1 = tp.paramBounds.mapConserve(this).asInstanceOf[List[TypeBounds]] - variance = -variance - derivedPolyType(tp, bounds1, this(tp.resultType)) - } - mapOverPoly - - case tp: RecType => - derivedRecType(tp, this(tp.parent)) - - case tp @ SuperType(thistp, supertp) => - derivedSuperType(tp, this(thistp), this(supertp)) - - case tp: LazyRef => - LazyRef(() => this(tp.ref)) - - case tp: ClassInfo => - mapClassInfo(tp) - - case tp: TypeVar => - val inst = tp.instanceOpt - if (inst.exists) apply(inst) else tp - - case tp: HKApply => - def mapArg(arg: Type, tparam: TypeParamInfo): Type = { - val saved = variance - variance *= tparam.paramVariance - try this(arg) - finally variance = saved - } - derivedAppliedType(tp, this(tp.tycon), - tp.args.zipWithConserve(tp.typeParams)(mapArg)) - - case tp: AndOrType => - derivedAndOrType(tp, this(tp.tp1), this(tp.tp2)) - - case tp: SkolemType => - tp - - case tp @ AnnotatedType(underlying, annot) => - val underlying1 = this(underlying) - if (underlying1 eq underlying) tp - else derivedAnnotatedType(tp, underlying1, mapOver(annot)) - - case tp @ WildcardType => - derivedWildcardType(tp, mapOver(tp.optBounds)) - - case tp: JavaArrayType => - derivedJavaArrayType(tp, this(tp.elemType)) - - case tp: ProtoType => - tp.map(this) - - case _ => - tp - } - } - - private def treeTypeMap = new TreeTypeMap(typeMap = this) - - def mapOver(syms: List[Symbol]): List[Symbol] = ctx.mapSymbols(syms, treeTypeMap) - - def mapOver(scope: Scope): Scope = { - val elems = scope.toList - val elems1 = mapOver(elems) - if (elems1 eq elems) scope - else newScopeWith(elems1: _*) - } - - def mapOver(annot: Annotation): Annotation = - annot.derivedAnnotation(mapOver(annot.tree)) - - def mapOver(tree: Tree): Tree = treeTypeMap(tree) - - /** Can be overridden. By default, only the prefix is mapped. */ - protected def mapClassInfo(tp: ClassInfo): Type = - derivedClassInfo(tp, this(tp.prefix)) - - def andThen(f: Type => Type): TypeMap = new TypeMap { - override def stopAtStatic = thisMap.stopAtStatic - def apply(tp: Type) = f(thisMap(tp)) - } - } - - /** A type map that maps also parents and self type of a ClassInfo */ - abstract class DeepTypeMap(implicit ctx: Context) extends TypeMap { - override def mapClassInfo(tp: ClassInfo) = { - val prefix1 = this(tp.prefix) - val parents1 = (tp.parents mapConserve this).asInstanceOf[List[TypeRef]] - val selfInfo1 = tp.selfInfo match { - case selfInfo: Type => this(selfInfo) - case selfInfo => selfInfo - } - tp.derivedClassInfo(prefix1, parents1, tp.decls, selfInfo1) - } - } - - @sharable object IdentityTypeMap extends TypeMap()(NoContext) { - override def stopAtStatic = true - def apply(tp: Type) = tp - } - - abstract class ApproximatingTypeMap(implicit ctx: Context) extends TypeMap { thisMap => - def approx(lo: Type = defn.NothingType, hi: Type = defn.AnyType) = - if (variance == 0) NoType - else apply(if (variance < 0) lo else hi) - - override protected def derivedSelect(tp: NamedType, pre: Type) = - if (pre eq tp.prefix) tp - else tp.info match { - case TypeAlias(alias) => apply(alias) // try to heal by following aliases - case _ => - if (pre.exists && !pre.isRef(defn.NothingClass) && variance > 0) tp.derivedSelect(pre) - else tp.info match { - case TypeBounds(lo, hi) => approx(lo, hi) - case _ => approx() - } - } - override protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type) = - if (parent.exists && info.exists) tp.derivedRefinedType(parent, tp.refinedName, info) - else approx(hi = parent) - override protected def derivedRecType(tp: RecType, parent: Type) = - if (parent.exists) tp.rebind(parent) - else approx() - override protected def derivedTypeAlias(tp: TypeAlias, alias: Type) = - if (alias.exists) tp.derivedTypeAlias(alias) - else approx(NoType, TypeBounds.empty) - override protected def derivedTypeBounds(tp: TypeBounds, lo: Type, hi: Type) = - if (lo.exists && hi.exists) tp.derivedTypeBounds(lo, hi) - else approx(NoType, - if (lo.exists) TypeBounds.lower(lo) - else if (hi.exists) TypeBounds.upper(hi) - else TypeBounds.empty) - override protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type) = - if (thistp.exists && supertp.exists) tp.derivedSuperType(thistp, supertp) - else NoType - override protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type = - if (tycon.exists && args.forall(_.exists)) tp.derivedAppliedType(tycon, args) - else approx() // This is rather coarse, but to do better is a bit complicated - override protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type) = - if (tp1.exists && tp2.exists) tp.derivedAndOrType(tp1, tp2) - else if (tp.isAnd) approx(hi = tp1 & tp2) // if one of tp1d, tp2d exists, it is the result of tp1d & tp2d - else approx(lo = tp1 & tp2) - override protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation) = - if (underlying.exists) tp.derivedAnnotatedType(underlying, annot) - else NoType - override protected def derivedWildcardType(tp: WildcardType, bounds: Type) = - if (bounds.exists) tp.derivedWildcardType(bounds) - else WildcardType - override protected def derivedClassInfo(tp: ClassInfo, pre: Type): Type = - if (pre.exists) tp.derivedClassInfo(pre) - else NoType - } - - // ----- TypeAccumulators ---------------------------------------------------- - - abstract class TypeAccumulator[T](implicit protected val ctx: Context) extends ((T, Type) => T) { - - protected def stopAtStatic = true - - def apply(x: T, tp: Type): T - - protected def applyToAnnot(x: T, annot: Annotation): T = x // don't go into annotations - - protected var variance = 1 - - protected def applyToPrefix(x: T, tp: NamedType) = { - val saved = variance - variance = 0 - val result = this(x, tp.prefix) - variance = saved - result - } - - def foldOver(x: T, tp: Type): T = tp match { - case tp: TypeRef => - if (stopAtStatic && tp.symbol.isStatic) x - else { - val tp1 = tp.prefix.lookupRefined(tp.name) - if (tp1.exists) this(x, tp1) else applyToPrefix(x, tp) - } - case tp: TermRef => - if (stopAtStatic && tp.currentSymbol.isStatic) x - else applyToPrefix(x, tp) - - case _: ThisType - | _: BoundType - | NoPrefix => x - - case tp: RefinedType => - this(this(x, tp.parent), tp.refinedInfo) - - case bounds @ TypeBounds(lo, hi) => - if (lo eq hi) { - val saved = variance - variance = variance * bounds.variance - val result = this(x, lo) - variance = saved - result - } - else { - variance = -variance - val y = this(x, lo) - variance = -variance - this(y, hi) - } - - case tp @ MethodType(pnames, ptypes) => - variance = -variance - val y = foldOver(x, ptypes) - variance = -variance - this(y, tp.resultType) - - case ExprType(restpe) => - this(x, restpe) - - case tp: PolyType => - variance = -variance - val y = foldOver(x, tp.paramBounds) - variance = -variance - this(y, tp.resultType) - - case tp: RecType => - this(x, tp.parent) - - case SuperType(thistp, supertp) => - this(this(x, thistp), supertp) - - case tp @ ClassInfo(prefix, _, _, _, _) => - this(x, prefix) - - case tp @ HKApply(tycon, args) => - def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T = - if (args.isEmpty) { - assert(tparams.isEmpty) - x - } - else { - val tparam = tparams.head - val saved = variance - variance *= tparam.paramVariance - val acc = - try this(x, args.head) - finally variance = saved - foldArgs(acc, tparams.tail, args.tail) - } - foldArgs(this(x, tycon), tp.typeParams, args) - - case tp: AndOrType => - this(this(x, tp.tp1), tp.tp2) - - case tp: SkolemType => - this(x, tp.info) - - case AnnotatedType(underlying, annot) => - this(applyToAnnot(x, annot), underlying) - - case tp: TypeVar => - this(x, tp.underlying) - - case tp: WildcardType => - this(x, tp.optBounds) - - case tp: JavaArrayType => - this(x, tp.elemType) - - case tp: LazyRef => - this(x, tp.ref) - - case tp: ProtoType => - tp.fold(x, this) - - case _ => x - } - - final def foldOver(x: T, ts: List[Type]): T = ts match { - case t :: ts1 => foldOver(apply(x, t), ts1) - case nil => x - } - } - - abstract class TypeTraverser(implicit ctx: Context) extends TypeAccumulator[Unit] { - def traverse(tp: Type): Unit - def apply(x: Unit, tp: Type): Unit = traverse(tp) - protected def traverseChildren(tp: Type) = foldOver((), tp) - } - - class ExistsAccumulator(p: Type => Boolean, forceLazy: Boolean = true)(implicit ctx: Context) extends TypeAccumulator[Boolean] { - override def stopAtStatic = false - def apply(x: Boolean, tp: Type) = - x || p(tp) || (forceLazy || !tp.isInstanceOf[LazyRef]) && foldOver(x, tp) - } - - class ForeachAccumulator(p: Type => Unit, override val stopAtStatic: Boolean)(implicit ctx: Context) extends TypeAccumulator[Unit] { - def apply(x: Unit, tp: Type): Unit = foldOver(p(tp), tp) - } - - class HasUnsafeNonAccumulator(implicit ctx: Context) extends TypeAccumulator[Boolean] { - def apply(x: Boolean, tp: Type) = x || tp.isUnsafeNonvariant || foldOver(x, tp) - } - - class NamedPartsAccumulator(p: NamedType => Boolean, excludeLowerBounds: Boolean = false) - (implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] { - override def stopAtStatic = false - def maybeAdd(x: mutable.Set[NamedType], tp: NamedType) = if (p(tp)) x += tp else x - val seen: mutable.Set[Type] = mutable.Set() - def apply(x: mutable.Set[NamedType], tp: Type): mutable.Set[NamedType] = - if (seen contains tp) x - else { - seen += tp - tp match { - case tp: TermRef => - apply(foldOver(maybeAdd(x, tp), tp), tp.underlying) - case tp: TypeRef => - foldOver(maybeAdd(x, tp), tp) - case TypeBounds(lo, hi) => - if (!excludeLowerBounds) apply(x, lo) - apply(x, hi) - case tp: ThisType => - apply(x, tp.tref) - case tp: ConstantType => - apply(x, tp.underlying) - case tp: MethodParam => - apply(x, tp.underlying) - case tp: PolyParam => - apply(x, tp.underlying) - case _ => - foldOver(x, tp) - } - } - } - - // ----- Name Filters -------------------------------------------------- - - /** A name filter selects or discards a member name of a type `pre`. - * To enable efficient caching, name filters have to satisfy the - * following invariant: If `keep` is a name filter, and `pre` has - * class `C` as a base class, then - * - * keep(pre, name) implies keep(C.this, name) - */ - abstract class NameFilter { - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean - } - - /** A filter for names of abstract types of a given type */ - object abstractTypeNameFilter extends NameFilter { - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = - name.isTypeName && { - val mbr = pre.nonPrivateMember(name) - (mbr.symbol is Deferred) && mbr.info.isInstanceOf[RealTypeBounds] - } - } - - /** A filter for names of abstract types of a given type */ - object nonClassTypeNameFilter extends NameFilter { - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = - name.isTypeName && { - val mbr = pre.member(name) - mbr.symbol.isType && !mbr.symbol.isClass - } - } - - /** A filter for names of deferred term definitions of a given type */ - object abstractTermNameFilter extends NameFilter { - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = - name.isTermName && pre.nonPrivateMember(name).hasAltWith(_.symbol is Deferred) - } - - object typeNameFilter extends NameFilter { - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = name.isTypeName - } - - object fieldFilter extends NameFilter { - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = - name.isTermName && (pre member name).hasAltWith(!_.symbol.is(Method)) - } - - object takeAllFilter extends NameFilter { - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true - } - - object implicitFilter extends NameFilter { - /** A dummy filter method. - * Implicit filtering is handled specially in computeMemberNames, so - * no post-filtering is needed. - */ - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true - } - - // ----- Exceptions ------------------------------------------------------------- - - class TypeError(msg: String) extends Exception(msg) - - class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name]) - extends TypeError( - s"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}") - - class MissingType(pre: Type, name: Name)(implicit ctx: Context) extends TypeError( - i"""cannot resolve reference to type $pre.$name - |the classfile defining the type might be missing from the classpath${otherReason(pre)}""") { - if (ctx.debug) printStackTrace() - } - - private def otherReason(pre: Type)(implicit ctx: Context): String = pre match { - case pre: ThisType if pre.givenSelfType.exists => - i"\nor the self type of $pre might not contain all transitive dependencies" - case _ => "" - } - - class CyclicReference private (val denot: SymDenotation) - extends TypeError(s"cyclic reference involving $denot") { - def show(implicit ctx: Context) = s"cyclic reference involving ${denot.show}" - } - - object CyclicReference { - def apply(denot: SymDenotation)(implicit ctx: Context): CyclicReference = { - val ex = new CyclicReference(denot) - if (!(ctx.mode is Mode.CheckCyclic)) { - cyclicErrors.println(ex.getMessage) - for (elem <- ex.getStackTrace take 200) - cyclicErrors.println(elem.toString) - } - ex - } - } - - class MergeError(msg: String, val tp1: Type, val tp2: Type) extends TypeError(msg) - - // ----- Debug --------------------------------------------------------- - - @sharable var debugTrace = false - - val watchList = List[String]( - ) map (_.toTypeName) - - def isWatched(tp: Type) = tp match { - case TypeRef(_, name) => watchList contains name - case _ => false - } - - // ----- Decorator implicits -------------------------------------------- - - implicit def decorateTypeApplications(tpe: Type): TypeApplications = new TypeApplications(tpe) -} diff --git a/src/dotty/tools/dotc/core/Uniques.scala b/src/dotty/tools/dotc/core/Uniques.scala deleted file mode 100644 index cb9670c69..000000000 --- a/src/dotty/tools/dotc/core/Uniques.scala +++ /dev/null @@ -1,128 +0,0 @@ -package dotty.tools.dotc -package core - -import Types._, Contexts._, util.Stats._, Hashable._, Names._ -import config.Config -import util.HashSet - -/** Defines operation `unique` for hash-consing types. - * Also defines specialized hash sets for hash consing uniques of a specific type. - * All sets offer a `enterIfNew` method which checks whether a type - * with the given parts exists already and creates a new one if not. - */ -object Uniques { - - private def recordCaching(tp: Type): Unit = recordCaching(tp.hash, tp.getClass) - private def recordCaching(h: Int, clazz: Class[_]): Unit = - if (h == NotCached) { - record("uncached-types") - record(s"uncached: $clazz") - } else { - record("cached-types") - record(s"cached: $clazz") - } - - def unique[T <: Type](tp: T)(implicit ctx: Context): T = { - if (monitored) recordCaching(tp) - if (tp.hash == NotCached) tp - else if (monitored) { - val size = ctx.uniques.size - val result = ctx.uniques.findEntryOrUpdate(tp).asInstanceOf[T] - if (ctx.uniques.size > size) record(s"fresh unique ${tp.getClass}") - result - } else ctx.uniques.findEntryOrUpdate(tp).asInstanceOf[T] - } /* !!! DEBUG - ensuring ( - result => tp.toString == result.toString || { - println(s"cache mismatch; tp = $tp, cached = $result") - false - } - ) - */ - - final class NamedTypeUniques extends HashSet[NamedType](Config.initialUniquesCapacity) with Hashable { - override def hash(x: NamedType): Int = x.hash - - private def findPrevious(h: Int, prefix: Type, name: Name): NamedType = { - var e = findEntryByHash(h) - while (e != null) { - if ((e.prefix eq prefix) && (e.name eq name)) return e - e = nextEntryByHash(h) - } - e - } - - def enterIfNew(prefix: Type, name: Name): NamedType = { - val h = doHash(name, prefix) - if (monitored) recordCaching(h, classOf[CachedTermRef]) - def newType = - if (name.isTypeName) new CachedTypeRef(prefix, name.asTypeName, h) - else new CachedTermRef(prefix, name.asTermName, h) - if (h == NotCached) newType - else { - val r = findPrevious(h, prefix, name) - if (r ne null) r else addEntryAfterScan(newType) - } - } - } - - final class TypeAliasUniques extends HashSet[TypeAlias](Config.initialUniquesCapacity) with Hashable { - override def hash(x: TypeAlias): Int = x.hash - - private def findPrevious(h: Int, alias: Type, variance: Int): TypeAlias = { - var e = findEntryByHash(h) - while (e != null) { - if ((e.alias eq alias) && (e.variance == variance)) return e - e = nextEntryByHash(h) - } - e - } - - def enterIfNew(alias: Type, variance: Int): TypeAlias = { - val h = doHash(variance, alias) - if (monitored) recordCaching(h, classOf[TypeAlias]) - def newAlias = new CachedTypeAlias(alias, variance, h) - if (h == NotCached) newAlias - else { - val r = findPrevious(h, alias, variance) - if (r ne null) r - else addEntryAfterScan(newAlias) - } - } - } - - final class RefinedUniques extends HashSet[RefinedType](Config.initialUniquesCapacity) with Hashable { - override val hashSeed = classOf[CachedRefinedType].hashCode // some types start life as CachedRefinedTypes, need to have same hash seed - override def hash(x: RefinedType): Int = x.hash - - private def findPrevious(h: Int, parent: Type, refinedName: Name, refinedInfo: Type): RefinedType = { - var e = findEntryByHash(h) - while (e != null) { - if ((e.parent eq parent) && (e.refinedName eq refinedName) && (e.refinedInfo eq refinedInfo)) - return e - e = nextEntryByHash(h) - } - e - } - - def enterIfNew(parent: Type, refinedName: Name, refinedInfo: Type): RefinedType = { - val h = doHash(refinedName, refinedInfo, parent) - def newType = new CachedRefinedType(parent, refinedName, refinedInfo, h) - if (monitored) recordCaching(h, classOf[CachedRefinedType]) - if (h == NotCached) newType - else { - val r = findPrevious(h, parent, refinedName, refinedInfo) - if (r ne null) r else addEntryAfterScan(newType) - } - } - - def enterIfNew(rt: RefinedType) = { - if (monitored) recordCaching(rt) - if (rt.hash == NotCached) rt - else { - val r = findPrevious(rt.hash, rt.parent, rt.refinedName, rt.refinedInfo) - if (r ne null) r else addEntryAfterScan(rt) - } - } - } -} diff --git a/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala b/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala deleted file mode 100644 index cad3a4132..000000000 --- a/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala +++ /dev/null @@ -1,88 +0,0 @@ -package dotty.tools -package dotc -package core -package classfile - -import java.lang.Float.intBitsToFloat -import java.lang.Double.longBitsToDouble - -import io.AbstractFile - -/** - * This class reads files byte per byte. Only used by ClassFileParser - * - * @author Philippe Altherr - * @version 1.0, 23/03/2004 - */ -class AbstractFileReader(val file: AbstractFile) { - - /** the buffer containing the file - */ - val buf: Array[Byte] = file.toByteArray - - /** the current input pointer - */ - var bp: Int = 0 - - /** return byte at offset 'pos' - */ - @throws(classOf[IndexOutOfBoundsException]) - def byteAt(pos: Int): Byte = buf(pos) - - /** read a byte - */ - @throws(classOf[IndexOutOfBoundsException]) - def nextByte: Byte = { - val b = buf(bp) - bp += 1 - b - } - - /** read some bytes - */ - def nextBytes(len: Int): Array[Byte] = { - bp += len - buf.slice(bp - len, bp) - } - - /** read a character - */ - def nextChar: Char = - (((nextByte & 0xff) << 8) + (nextByte & 0xff)).toChar - - /** read an integer - */ - def nextInt: Int = - ((nextByte & 0xff) << 24) + ((nextByte & 0xff) << 16) + - ((nextByte & 0xff) << 8) + (nextByte & 0xff) - - - /** extract a character at position bp from buf - */ - def getChar(mybp: Int): Char = - (((buf(mybp) & 0xff) << 8) + (buf(mybp + 1) & 0xff)).toChar - - /** extract an integer at position bp from buf - */ - def getInt(mybp: Int): Int = - ((buf(mybp ) & 0xff) << 24) + ((buf(mybp + 1) & 0xff) << 16) + - ((buf(mybp + 2) & 0xff) << 8) + (buf(mybp + 3) & 0xff) - - /** extract a long integer at position bp from buf - */ - def getLong(mybp: Int): Long = - (getInt(mybp).toLong << 32) + (getInt(mybp + 4) & 0xffffffffL) - - /** extract a float at position bp from buf - */ - def getFloat(mybp: Int): Float = intBitsToFloat(getInt(mybp)) - - /** extract a double at position bp from buf - */ - def getDouble(mybp: Int): Double = longBitsToDouble(getLong(mybp)) - - /** skip next 'n' bytes - */ - def skip(n: Int): Unit = { bp += n } - -} diff --git a/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala b/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala deleted file mode 100644 index badd9e560..000000000 --- a/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala +++ /dev/null @@ -1,221 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2007-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ -package dotty.tools.dotc.core.classfile - -object ByteCodecs { - - def avoidZero(src: Array[Byte]): Array[Byte] = { - var i = 0 - val srclen = src.length - var count = 0 - while (i < srclen) { - if (src(i) == 0x7f) count += 1 - i += 1 - } - val dst = new Array[Byte](srclen + count) - i = 0 - var j = 0 - while (i < srclen) { - val in = src(i) - if (in == 0x7f) { - dst(j) = (0xc0).toByte - dst(j + 1) = (0x80).toByte - j += 2 - } else { - dst(j) = (in + 1).toByte - j += 1 - } - i += 1 - } - dst - } - - def regenerateZero(src: Array[Byte]): Int = { - var i = 0 - val srclen = src.length - var j = 0 - while (i < srclen) { - val in: Int = src(i) & 0xff - if (in == 0xc0 && (src(i + 1) & 0xff) == 0x80) { - src(j) = 0x7f - i += 2 - } else if (in == 0) { - src(j) = 0x7f - i += 1 - } else { - src(j) = (in - 1).toByte - i += 1 - } - j += 1 - } - j - } - - def encode8to7(src: Array[Byte]): Array[Byte] = { - val srclen = src.length - val dstlen = (srclen * 8 + 6) / 7 - val dst = new Array[Byte](dstlen) - var i = 0 - var j = 0 - while (i + 6 < srclen) { - var in: Int = src(i) & 0xff - dst(j) = (in & 0x7f).toByte - var out: Int = in >>> 7 - in = src(i + 1) & 0xff - dst(j + 1) = (out | (in << 1) & 0x7f).toByte - out = in >>> 6 - in = src(i + 2) & 0xff - dst(j + 2) = (out | (in << 2) & 0x7f).toByte - out = in >>> 5 - in = src(i + 3) & 0xff - dst(j + 3) = (out | (in << 3) & 0x7f).toByte - out = in >>> 4 - in = src(i + 4) & 0xff - dst(j + 4) = (out | (in << 4) & 0x7f).toByte - out = in >>> 3 - in = src(i + 5) & 0xff - dst(j + 5) = (out | (in << 5) & 0x7f).toByte - out = in >>> 2 - in = src(i + 6) & 0xff - dst(j + 6) = (out | (in << 6) & 0x7f).toByte - out = in >>> 1 - dst(j + 7) = out.toByte - i += 7 - j += 8 - } - if (i < srclen) { - var in: Int = src(i) & 0xff - dst(j) = (in & 0x7f).toByte; j += 1 - var out: Int = in >>> 7 - if (i + 1 < srclen) { - in = src(i + 1) & 0xff - dst(j) = (out | (in << 1) & 0x7f).toByte; j += 1 - out = in >>> 6 - if (i + 2 < srclen) { - in = src(i + 2) & 0xff - dst(j) = (out | (in << 2) & 0x7f).toByte; j += 1 - out = in >>> 5 - if (i + 3 < srclen) { - in = src(i + 3) & 0xff - dst(j) = (out | (in << 3) & 0x7f).toByte; j += 1 - out = in >>> 4 - if (i + 4 < srclen) { - in = src(i + 4) & 0xff - dst(j) = (out | (in << 4) & 0x7f).toByte; j += 1 - out = in >>> 3 - if (i + 5 < srclen) { - in = src(i + 5) & 0xff - dst(j) = (out | (in << 5) & 0x7f).toByte; j += 1 - out = in >>> 2 - } - } - } - } - } - if (j < dstlen) dst(j) = out.toByte - } - dst - } - - def decode7to8(src: Array[Byte], srclen: Int): Int = { - var i = 0 - var j = 0 - val dstlen = (srclen * 7 + 7) / 8 - while (i + 7 < srclen) { - var out: Int = src(i) - var in: Byte = src(i + 1) - src(j) = (out | (in & 0x01) << 7).toByte - out = in >>> 1 - in = src(i + 2) - src(j + 1) = (out | (in & 0x03) << 6).toByte - out = in >>> 2 - in = src(i + 3) - src(j + 2) = (out | (in & 0x07) << 5).toByte - out = in >>> 3 - in = src(i + 4) - src(j + 3) = (out | (in & 0x0f) << 4).toByte - out = in >>> 4 - in = src(i + 5) - src(j + 4) = (out | (in & 0x1f) << 3).toByte - out = in >>> 5 - in = src(i + 6) - src(j + 5) = (out | (in & 0x3f) << 2).toByte - out = in >>> 6 - in = src(i + 7) - src(j + 6) = (out | in << 1).toByte - i += 8 - j += 7 - } - if (i < srclen) { - var out: Int = src(i) - if (i + 1 < srclen) { - var in: Byte = src(i + 1) - src(j) = (out | (in & 0x01) << 7).toByte; j += 1 - out = in >>> 1 - if (i + 2 < srclen) { - in = src(i + 2) - src(j) = (out | (in & 0x03) << 6).toByte; j += 1 - out = in >>> 2 - if (i + 3 < srclen) { - in = src(i + 3) - src(j) = (out | (in & 0x07) << 5).toByte; j += 1 - out = in >>> 3 - if (i + 4 < srclen) { - in = src(i + 4) - src(j) = (out | (in & 0x0f) << 4).toByte; j += 1 - out = in >>> 4 - if (i + 5 < srclen) { - in = src(i + 5) - src(j) = (out | (in & 0x1f) << 3).toByte; j += 1 - out = in >>> 5 - if (i + 6 < srclen) { - in = src(i + 6) - src(j) = (out | (in & 0x3f) << 2).toByte; j += 1 - out = in >>> 6 - } - } - } - } - } - } - if (j < dstlen) src(j) = out.toByte - } - dstlen - } - - def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs)) - - /** - * Destructively decodes array xs and returns the length of the decoded array. - * - * Sometimes returns (length + 1) of the decoded array. Example: - * - * scala> val enc = reflect.generic.ByteCodecs.encode(Array(1,2,3)) - * enc: Array[Byte] = Array(2, 5, 13, 1) - * - * scala> reflect.generic.ByteCodecs.decode(enc) - * res43: Int = 4 - * - * scala> enc - * res44: Array[Byte] = Array(1, 2, 3, 0) - * - * However, this does not always happen. - */ - def decode(xs: Array[Byte]): Int = { - val len = regenerateZero(xs) - decode7to8(xs, len) - } -} - - - - - - - - diff --git a/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala b/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala deleted file mode 100644 index dd29fa49d..000000000 --- a/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala +++ /dev/null @@ -1,378 +0,0 @@ -package dotty.tools.dotc -package core -package classfile - -import scala.annotation.switch - -object ClassfileConstants { - - final val JAVA_MAGIC = 0xCAFEBABE - final val JAVA_MAJOR_VERSION = 45 - final val JAVA_MINOR_VERSION = 3 - - /** (see http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html) - * - * If the `ACC_INTERFACE` flag is set, the `ACC_ABSTRACT` flag must also - * be set (ch. 2.13.1). - * - * A class file cannot have both its `ACC_FINAL` and `ACC_ABSTRACT` flags - * set (ch. 2.8.2). - * - * A field may have at most one of its `ACC_PRIVATE`, `ACC_PROTECTED`, - * `ACC_PUBLIC` flags set (ch. 2.7.4). - * - * A field may not have both its `ACC_FINAL` and `ACC_VOLATILE` flags set - * (ch. 2.9.1). - * - * If a method has its `ACC_ABSTRACT` flag set it must not have any of its - * `ACC_FINAL`, `ACC_NATIVE`, `ACC_PRIVATE`, `ACC_STATIC`, `ACC_STRICT`, - * or `ACC_SYNCHRONIZED` flags set (ch. 2.13.3.2). - * - * All interface methods must have their `ACC_ABSTRACT` and - * `ACC_PUBLIC` flags set. - * - * Note for future reference: see this thread on ACC_SUPER and - * how its enforcement differs on the android vm. - * https://groups.google.com/forum/?hl=en#!topic/jvm-languages/jVhzvq8-ZIk - * - */ // Class Field Method - final val JAVA_ACC_PUBLIC = 0x0001 // X X X - final val JAVA_ACC_PRIVATE = 0x0002 // X X - final val JAVA_ACC_PROTECTED = 0x0004 // X X - final val JAVA_ACC_STATIC = 0x0008 // X X - final val JAVA_ACC_FINAL = 0x0010 // X X X - final val JAVA_ACC_SUPER = 0x0020 // X - final val JAVA_ACC_SYNCHRONIZED = 0x0020 // X - final val JAVA_ACC_VOLATILE = 0x0040 // X - final val JAVA_ACC_BRIDGE = 0x0040 // X - final val JAVA_ACC_TRANSIENT = 0x0080 // X - final val JAVA_ACC_VARARGS = 0x0080 // X - final val JAVA_ACC_NATIVE = 0x0100 // X - final val JAVA_ACC_INTERFACE = 0x0200 // X - final val JAVA_ACC_ABSTRACT = 0x0400 // X X - final val JAVA_ACC_STRICT = 0x0800 // X - final val JAVA_ACC_SYNTHETIC = 0x1000 // X X X - final val JAVA_ACC_ANNOTATION = 0x2000 // X - final val JAVA_ACC_ENUM = 0x4000 // X X - - // tags describing the type of a literal in the constant pool - final val CONSTANT_UTF8 = 1 - final val CONSTANT_UNICODE = 2 - final val CONSTANT_INTEGER = 3 - final val CONSTANT_FLOAT = 4 - final val CONSTANT_LONG = 5 - final val CONSTANT_DOUBLE = 6 - final val CONSTANT_CLASS = 7 - final val CONSTANT_STRING = 8 - final val CONSTANT_FIELDREF = 9 - final val CONSTANT_METHODREF = 10 - final val CONSTANT_INTFMETHODREF = 11 - final val CONSTANT_NAMEANDTYPE = 12 - - final val CONSTANT_METHODHANDLE = 15 - final val CONSTANT_METHODTYPE = 16 - final val CONSTANT_INVOKEDYNAMIC = 18 - - // tags describing the type of a literal in attribute values - final val BYTE_TAG = 'B' - final val CHAR_TAG = 'C' - final val DOUBLE_TAG = 'D' - final val FLOAT_TAG = 'F' - final val INT_TAG = 'I' - final val LONG_TAG = 'J' - final val SHORT_TAG = 'S' - final val BOOL_TAG = 'Z' - final val STRING_TAG = 's' - final val ENUM_TAG = 'e' - final val CLASS_TAG = 'c' - final val ARRAY_TAG = '[' - final val VOID_TAG = 'V' - final val TVAR_TAG = 'T' - final val OBJECT_TAG = 'L' - final val ANNOTATION_TAG = '@' - final val SCALA_NOTHING = "scala.runtime.Nothing$" - final val SCALA_NULL = "scala.runtime.Null$" - - - // tags describing the type of newarray - final val T_BOOLEAN = 4 - final val T_CHAR = 5 - final val T_FLOAT = 6 - final val T_DOUBLE = 7 - final val T_BYTE = 8 - final val T_SHORT = 9 - final val T_INT = 10 - final val T_LONG = 11 - - // JVM mnemonics - final val nop = 0x00 - final val aconst_null = 0x01 - final val iconst_m1 = 0x02 - - final val iconst_0 = 0x03 - final val iconst_1 = 0x04 - final val iconst_2 = 0x05 - final val iconst_3 = 0x06 - final val iconst_4 = 0x07 - final val iconst_5 = 0x08 - - final val lconst_0 = 0x09 - final val lconst_1 = 0x0a - final val fconst_0 = 0x0b - final val fconst_1 = 0x0c - final val fconst_2 = 0x0d - final val dconst_0 = 0x0e - final val dconst_1 = 0x0f - - final val bipush = 0x10 - final val sipush = 0x11 - final val ldc = 0x12 - final val ldc_w = 0x13 - final val ldc2_w = 0x14 - - final val iload = 0x15 - final val lload = 0x16 - final val fload = 0x17 - final val dload = 0x18 - final val aload = 0x19 - - final val iload_0 = 0x1a - final val iload_1 = 0x1b - final val iload_2 = 0x1c - final val iload_3 = 0x1d - final val lload_0 = 0x1e - final val lload_1 = 0x1f - final val lload_2 = 0x20 - final val lload_3 = 0x21 - final val fload_0 = 0x22 - final val fload_1 = 0x23 - final val fload_2 = 0x24 - final val fload_3 = 0x25 - final val dload_0 = 0x26 - final val dload_1 = 0x27 - final val dload_2 = 0x28 - final val dload_3 = 0x29 - final val aload_0 = 0x2a - final val aload_1 = 0x2b - final val aload_2 = 0x2c - final val aload_3 = 0x2d - final val iaload = 0x2e - final val laload = 0x2f - final val faload = 0x30 - final val daload = 0x31 - final val aaload = 0x32 - final val baload = 0x33 - final val caload = 0x34 - final val saload = 0x35 - - final val istore = 0x36 - final val lstore = 0x37 - final val fstore = 0x38 - final val dstore = 0x39 - final val astore = 0x3a - final val istore_0 = 0x3b - final val istore_1 = 0x3c - final val istore_2 = 0x3d - final val istore_3 = 0x3e - final val lstore_0 = 0x3f - final val lstore_1 = 0x40 - final val lstore_2 = 0x41 - final val lstore_3 = 0x42 - final val fstore_0 = 0x43 - final val fstore_1 = 0x44 - final val fstore_2 = 0x45 - final val fstore_3 = 0x46 - final val dstore_0 = 0x47 - final val dstore_1 = 0x48 - final val dstore_2 = 0x49 - final val dstore_3 = 0x4a - final val astore_0 = 0x4b - final val astore_1 = 0x4c - final val astore_2 = 0x4d - final val astore_3 = 0x4e - final val iastore = 0x4f - final val lastore = 0x50 - final val fastore = 0x51 - final val dastore = 0x52 - final val aastore = 0x53 - final val bastore = 0x54 - final val castore = 0x55 - final val sastore = 0x56 - - final val pop = 0x57 - final val pop2 = 0x58 - final val dup = 0x59 - final val dup_x1 = 0x5a - final val dup_x2 = 0x5b - final val dup2 = 0x5c - final val dup2_x1 = 0x5d - final val dup2_x2 = 0x5e - final val swap = 0x5f - - final val iadd = 0x60 - final val ladd = 0x61 - final val fadd = 0x62 - final val dadd = 0x63 - final val isub = 0x64 - final val lsub = 0x65 - final val fsub = 0x66 - final val dsub = 0x67 - final val imul = 0x68 - final val lmul = 0x69 - final val fmul = 0x6a - final val dmul = 0x6b - final val idiv = 0x6c - final val ldiv = 0x6d - final val fdiv = 0x6e - final val ddiv = 0x6f - final val irem = 0x70 - final val lrem = 0x71 - final val frem = 0x72 - final val drem = 0x73 - - final val ineg = 0x74 - final val lneg = 0x75 - final val fneg = 0x76 - final val dneg = 0x77 - - final val ishl = 0x78 - final val lshl = 0x79 - final val ishr = 0x7a - final val lshr = 0x7b - final val iushr = 0x7c - final val lushr = 0x7d - final val iand = 0x7e - final val land = 0x7f - final val ior = 0x80 - final val lor = 0x81 - final val ixor = 0x82 - final val lxor = 0x83 - final val iinc = 0x84 - - final val i2l = 0x85 - final val i2f = 0x86 - final val i2d = 0x87 - final val l2i = 0x88 - final val l2f = 0x89 - final val l2d = 0x8a - final val f2i = 0x8b - final val f2l = 0x8c - final val f2d = 0x8d - final val d2i = 0x8e - final val d2l = 0x8f - final val d2f = 0x90 - final val i2b = 0x91 - final val i2c = 0x92 - final val i2s = 0x93 - - final val lcmp = 0x94 - final val fcmpl = 0x95 - final val fcmpg = 0x96 - final val dcmpl = 0x97 - final val dcmpg = 0x98 - - final val ifeq = 0x99 - final val ifne = 0x9a - final val iflt = 0x9b - final val ifge = 0x9c - final val ifgt = 0x9d - final val ifle = 0x9e - final val if_icmpeq = 0x9f - final val if_icmpne = 0xa0 - final val if_icmplt = 0xa1 - final val if_icmpge = 0xa2 - final val if_icmpgt = 0xa3 - final val if_icmple = 0xa4 - final val if_acmpeq = 0xa5 - final val if_acmpne = 0xa6 - final val goto = 0xa7 - final val jsr = 0xa8 - final val ret = 0xa9 - final val tableswitch = 0xaa - final val lookupswitch = 0xab - final val ireturn = 0xac - final val lreturn = 0xad - final val freturn = 0xae - final val dreturn = 0xaf - final val areturn = 0xb0 - final val return_ = 0xb1 - - final val getstatic = 0xb2 - final val putstatic = 0xb3 - final val getfield = 0xb4 - final val putfield = 0xb5 - - final val invokevirtual = 0xb6 - final val invokespecial = 0xb7 - final val invokestatic = 0xb8 - final val invokeinterface = 0xb9 - final val xxxunusedxxxx = 0xba - - final val new_ = 0xbb - final val newarray = 0xbc - final val anewarray = 0xbd - final val arraylength = 0xbe - final val athrow = 0xbf - final val checkcast = 0xc0 - final val instanceof = 0xc1 - final val monitorenter = 0xc2 - final val monitorexit = 0xc3 - final val wide = 0xc4 - final val multianewarray = 0xc5 - final val ifnull = 0xc6 - final val ifnonnull = 0xc7 - final val goto_w = 0xc8 - final val jsr_w = 0xc9 - - // reserved opcodes - final val breakpoint = 0xca - final val impdep1 = 0xfe - final val impdep2 = 0xff - - import Flags._ - abstract class FlagTranslation { - - protected def baseFlags(jflags: Int) = EmptyFlags - protected def isClass: Boolean = false - - private def translateFlag(jflag: Int): FlagSet = (jflag: @switch) match { - case JAVA_ACC_PRIVATE => Private - case JAVA_ACC_PROTECTED => Protected - case JAVA_ACC_FINAL => Final - case JAVA_ACC_SYNTHETIC => Synthetic - case JAVA_ACC_STATIC => JavaStatic - case JAVA_ACC_ABSTRACT => if (isClass) Abstract else Deferred - case JAVA_ACC_INTERFACE => PureInterfaceCreationFlags | JavaDefined - case _ => EmptyFlags - } - - private def addFlag(base: FlagSet, jflag: Int): FlagSet = - if (jflag == 0) base else base | translateFlag(jflag) - - private def translateFlags(jflags: Int, baseFlags: FlagSet): FlagSet = { - val nflags = - if ((jflags & JAVA_ACC_ANNOTATION) == 0) jflags - else jflags & ~(JAVA_ACC_ABSTRACT | JAVA_ACC_INTERFACE) // annotations are neither abstract nor interfaces - var res: FlagSet = baseFlags | JavaDefined - res = addFlag(res, nflags & JAVA_ACC_PRIVATE) - res = addFlag(res, nflags & JAVA_ACC_PROTECTED) - res = addFlag(res, nflags & JAVA_ACC_FINAL) - res = addFlag(res, nflags & JAVA_ACC_SYNTHETIC) - res = addFlag(res, nflags & JAVA_ACC_STATIC) - res = addFlag(res, nflags & JAVA_ACC_ABSTRACT) - res = addFlag(res, nflags & JAVA_ACC_INTERFACE) - res - } - - def flags(jflags: Int): FlagSet = translateFlags(jflags, baseFlags(jflags)) - } - val classTranslation = new FlagTranslation { - override def isClass = true - } - val fieldTranslation = new FlagTranslation { - override def baseFlags(jflags: Int) = if ((jflags & JAVA_ACC_FINAL) == 0) Mutable else EmptyFlags - } - val methodTranslation = new FlagTranslation { - override def baseFlags(jflags: Int) = if ((jflags & JAVA_ACC_BRIDGE) != 0) Bridge else EmptyFlags - } -} diff --git a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala deleted file mode 100644 index 97a82e80d..000000000 --- a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ /dev/null @@ -1,1100 +0,0 @@ -package dotty.tools -package dotc -package core -package classfile - -import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._ -import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._ -import ast.tpd._ -import java.io.{ File, IOException } -import java.lang.Integer.toHexString -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.{ ListBuffer, ArrayBuffer } -import scala.annotation.switch -import typer.Checking.checkNonCyclic -import io.AbstractFile -import scala.util.control.NonFatal - -object ClassfileParser { - /** Marker trait for unpicklers that can be embedded in classfiles. */ - trait Embedded -} - -class ClassfileParser( - classfile: AbstractFile, - classRoot: ClassDenotation, - moduleRoot: ClassDenotation)(ictx: Context) { - - import ClassfileConstants._ - import ClassfileParser._ - - protected val in = new AbstractFileReader(classfile) - - protected val staticModule: Symbol = moduleRoot.sourceModule(ictx) - - protected val instanceScope: MutableScope = newScope // the scope of all instance definitions - protected val staticScope: MutableScope = newScope // the scope of all static definitions - protected var pool: ConstantPool = _ // the classfile's constant pool - - protected var currentClassName: Name = _ // JVM name of the current class - protected var classTParams = Map[Name,Symbol]() - - classRoot.info = (new NoCompleter).withDecls(instanceScope) - moduleRoot.info = (new NoCompleter).withDecls(staticScope).withSourceModule(_ => staticModule) - - private def currentIsTopLevel(implicit ctx: Context) = classRoot.owner is Flags.PackageClass - - private def mismatchError(c: Symbol) = - throw new IOException(s"class file '${in.file}' has location not matching its contents: contains $c") - - def run()(implicit ctx: Context): Option[Embedded] = try { - ctx.debuglog("[class] >> " + classRoot.fullName) - parseHeader - this.pool = new ConstantPool - parseClass() - } catch { - case e: RuntimeException => - if (ctx.debug) e.printStackTrace() - throw new IOException( - i"""class file $classfile is broken, reading aborted with ${e.getClass} - |${Option(e.getMessage).getOrElse("")}""") - } - - private def parseHeader(): Unit = { - val magic = in.nextInt - if (magic != JAVA_MAGIC) - throw new IOException(s"class file '${in.file}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}") - val minorVersion = in.nextChar.toInt - val majorVersion = in.nextChar.toInt - if ((majorVersion < JAVA_MAJOR_VERSION) || - ((majorVersion == JAVA_MAJOR_VERSION) && - (minorVersion < JAVA_MINOR_VERSION))) - throw new IOException( - s"class file '${in.file}' has unknown version $majorVersion.$minorVersion, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION") - } - - /** Return the class symbol of the given name. */ - def classNameToSymbol(name: Name)(implicit ctx: Context): Symbol = innerClasses.get(name) match { - case Some(entry) => innerClasses.classSymbol(entry.externalName) - case None => ctx.requiredClass(name) - } - - var sawPrivateConstructor = false - - def parseClass()(implicit ctx: Context): Option[Embedded] = { - val jflags = in.nextChar - val isAnnotation = hasAnnotation(jflags) - val sflags = classTranslation.flags(jflags) - val isEnum = (jflags & JAVA_ACC_ENUM) != 0 - val nameIdx = in.nextChar - currentClassName = pool.getClassName(nameIdx) - - if (currentIsTopLevel) { - val c = pool.getClassSymbol(nameIdx) - if (c != classRoot.symbol) mismatchError(c) - } - - addEnclosingTParams() - - /** Parse parents for Java classes. For Scala, return AnyRef, since the real type will be unpickled. - * Updates the read pointer of 'in'. */ - def parseParents: List[Type] = { - val superType = if (isAnnotation) { in.nextChar; defn.AnnotationType } - else pool.getSuperClass(in.nextChar).typeRef - val ifaceCount = in.nextChar - var ifaces = for (i <- (0 until ifaceCount).toList) yield pool.getSuperClass(in.nextChar).typeRef - // Dotty deviation: was - // var ifaces = for (i <- List.range(0 until ifaceCount)) ... - // This does not typecheck because the type parameter of List is now lower-bounded by Int | Char. - // Consequently, no best implicit for the "Integral" evidence parameter of "range" - // is found. If we treat constant subtyping specially, we might be able - // to do something there. But in any case, the until should be more efficient. - - if (isAnnotation) ifaces = defn.ClassfileAnnotationType :: ifaces - superType :: ifaces - } - - val result = unpickleOrParseInnerClasses() - if (!result.isDefined) { - var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol) - // might be reassigned by later parseAttributes - val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol) - - enterOwnInnerClasses - - classRoot.setFlag(sflags) - moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags) - setPrivateWithin(classRoot, jflags) - setPrivateWithin(moduleRoot, jflags) - setPrivateWithin(moduleRoot.sourceModule, jflags) - - for (i <- 0 until in.nextChar) parseMember(method = false) - for (i <- 0 until in.nextChar) parseMember(method = true) - classInfo = parseAttributes(classRoot.symbol, classInfo) - if (isAnnotation) addAnnotationConstructor(classInfo) - - val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot) - if (companionClassMethod.exists) companionClassMethod.entered - val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot) - if (companionModuleMethod.exists) companionModuleMethod.entered - - setClassInfo(classRoot, classInfo) - setClassInfo(moduleRoot, staticInfo) - } - - // eager load java enum definitions for exhaustivity check of pattern match - if (isEnum) { - instanceScope.toList.map(_.ensureCompleted()) - staticScope.toList.map(_.ensureCompleted()) - classRoot.setFlag(Flags.Enum) - moduleRoot.setFlag(Flags.Enum) - } - - result - } - - /** Add type parameters of enclosing classes */ - def addEnclosingTParams()(implicit ctx: Context): Unit = { - var sym = classRoot.owner - while (sym.isClass && !(sym is Flags.ModuleClass)) { - for (tparam <- sym.typeParams) { - classTParams = classTParams.updated(tparam.name.unexpandedName, tparam) - } - sym = sym.owner - } - } - - def parseMember(method: Boolean)(implicit ctx: Context): Unit = { - val start = indexCoord(in.bp) - val jflags = in.nextChar - val sflags = - if (method) Flags.Method | methodTranslation.flags(jflags) - else fieldTranslation.flags(jflags) - val name = pool.getName(in.nextChar) - if (!(sflags is Flags.Private) || name == nme.CONSTRUCTOR || ctx.settings.optimise.value) { - val member = ctx.newSymbol( - getOwner(jflags), name, sflags, memberCompleter, coord = start) - getScope(jflags).enter(member) - } - // skip rest of member for now - in.nextChar // info - skipAttributes - } - - val memberCompleter = new LazyType { - - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val oldbp = in.bp - try { - in.bp = denot.symbol.coord.toIndex - val sym = denot.symbol - val jflags = in.nextChar - val isEnum = (jflags & JAVA_ACC_ENUM) != 0 - val name = pool.getName(in.nextChar) - val isConstructor = name eq nme.CONSTRUCTOR - - /** Strip leading outer param from constructor. - * Todo: Also strip trailing access tag for private inner constructors? - */ - def stripOuterParamFromConstructor() = innerClasses.get(currentClassName) match { - case Some(entry) if !isStatic(entry.jflags) => - val mt @ MethodType(paramnames, paramtypes) = denot.info - denot.info = mt.derivedMethodType(paramnames.tail, paramtypes.tail, mt.resultType) - case _ => - } - - /** Make return type of constructor be the enclosing class type, - * and make constructor type polymorphic in the type parameters of the class - */ - def normalizeConstructorInfo() = { - val mt @ MethodType(paramnames, paramtypes) = denot.info - val rt = classRoot.typeRef appliedTo (classRoot.typeParams map (_.typeRef)) - denot.info = mt.derivedMethodType(paramnames, paramtypes, rt) - addConstructorTypeParams(denot) - } - - denot.info = pool.getType(in.nextChar) - if (isEnum) denot.info = ConstantType(Constant(sym)) - if (isConstructor) stripOuterParamFromConstructor() - setPrivateWithin(denot, jflags) - denot.info = translateTempPoly(parseAttributes(sym, denot.info)) - if (isConstructor) normalizeConstructorInfo() - - if ((denot is Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0) - denot.info = arrayToRepeated(denot.info) - - // seal java enums - if (isEnum) { - val enumClass = sym.owner.linkedClass - if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed) - enumClass.addAnnotation(Annotation.makeChild(sym)) - } - } finally { - in.bp = oldbp - } - } - } - - /** Map direct references to Object to references to Any */ - final def objToAny(tp: Type)(implicit ctx: Context) = - if (tp.isDirectRef(defn.ObjectClass) && !ctx.phase.erasedTypes) defn.AnyType else tp - - private def sigToType(sig: TermName, owner: Symbol = null)(implicit ctx: Context): Type = { - var index = 0 - val end = sig.length - def accept(ch: Char): Unit = { - assert(sig(index) == ch, (sig(index), ch)) - index += 1 - } - def subName(isDelimiter: Char => Boolean): TermName = { - val start = index - while (!isDelimiter(sig(index))) { index += 1 } - sig.slice(start, index) - } - // Warning: sigToType contains nested completers which might be forced in a later run! - // So local methods need their own ctx parameters. - def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { - val tag = sig(index); index += 1 - (tag: @switch) match { - case BYTE_TAG => defn.ByteType - case CHAR_TAG => defn.CharType - case DOUBLE_TAG => defn.DoubleType - case FLOAT_TAG => defn.FloatType - case INT_TAG => defn.IntType - case LONG_TAG => defn.LongType - case SHORT_TAG => defn.ShortType - case VOID_TAG => defn.UnitType - case BOOL_TAG => defn.BooleanType - case 'L' => - def processInner(tp: Type): Type = tp match { - case tp: TypeRef if !(tp.symbol.owner is Flags.ModuleClass) => - TypeRef(processInner(tp.prefix.widen), tp.name) - case _ => - tp - } - def processClassType(tp: Type): Type = tp match { - case tp: TypeRef => - if (sig(index) == '<') { - accept('<') - var tp1: Type = tp - var formals = tp.typeParamSymbols - while (sig(index) != '>') { - sig(index) match { - case variance @ ('+' | '-' | '*') => - index += 1 - val bounds = variance match { - case '+' => objToAny(TypeBounds.upper(sig2type(tparams, skiptvs))) - case '-' => - val tp = sig2type(tparams, skiptvs) - // sig2type seems to return AnyClass regardless of the situation: - // we don't want Any as a LOWER bound. - if (tp.isDirectRef(defn.AnyClass)) TypeBounds.empty - else TypeBounds.lower(tp) - case '*' => TypeBounds.empty - } - tp1 = RefinedType(tp1, formals.head.name, bounds) - case _ => - tp1 = RefinedType(tp1, formals.head.name, TypeAlias(sig2type(tparams, skiptvs))) - } - formals = formals.tail - } - accept('>') - tp1 - } else tp - case tp => - assert(sig(index) != '<', tp) - tp - } - - val classSym = classNameToSymbol(subName(c => c == ';' || c == '<')) - var tpe = processClassType(processInner(classSym.typeRef)) - while (sig(index) == '.') { - accept('.') - val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName - val clazz = tpe.member(name).symbol - tpe = processClassType(processInner(clazz.typeRef)) - } - accept(';') - tpe - case ARRAY_TAG => - while ('0' <= sig(index) && sig(index) <= '9') index += 1 - var elemtp = sig2type(tparams, skiptvs) - // make unbounded Array[T] where T is a type variable into Ar ray[T with Object] - // (this is necessary because such arrays have a representation which is incompatible - // with arrays of primitive types. - // NOTE that the comparison to Object only works for abstract types bounded by classes that are strict subclasses of Object - // if the bound is exactly Object, it will have been converted to Any, and the comparison will fail - // see also RestrictJavaArraysMap (when compiling java sources directly) - if (elemtp.typeSymbol.isAbstractType && !(elemtp.derivesFrom(defn.ObjectClass))) { - elemtp = AndType(elemtp, defn.ObjectType) - } - defn.ArrayOf(elemtp) - case '(' => - // we need a method symbol. given in line 486 by calling getType(methodSym, ..) - val paramtypes = new ListBuffer[Type]() - var paramnames = new ListBuffer[TermName]() - while (sig(index) != ')') { - paramnames += nme.syntheticParamName(paramtypes.length) - paramtypes += objToAny(sig2type(tparams, skiptvs)) - } - index += 1 - val restype = sig2type(tparams, skiptvs) - JavaMethodType(paramnames.toList, paramtypes.toList)(_ => restype) - case 'T' => - val n = subName(';'.==).toTypeName - index += 1 - //assert(tparams contains n, s"classTparams = $classTParams, tparams = $tparams, key = $n") - if (skiptvs) defn.AnyType else tparams(n).typeRef - } - } // sig2type(tparams, skiptvs) - - def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { - val ts = new ListBuffer[Type] - while (sig(index) == ':') { - index += 1 - if (sig(index) != ':') // guard against empty class bound - ts += objToAny(sig2type(tparams, skiptvs)) - } - TypeBounds.upper(((NoType: Type) /: ts)(_ & _) orElse defn.AnyType) - } - - var tparams = classTParams - - def typeParamCompleter(start: Int) = new LazyType { - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val savedIndex = index - try { - index = start - denot.info = - checkNonCyclic( // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles - denot.symbol, - sig2typeBounds(tparams, skiptvs = false), - reportErrors = false) - } finally { - index = savedIndex - } - } - } - - val newTParams = new ListBuffer[Symbol]() - if (sig(index) == '<') { - assert(owner != null) - index += 1 - val start = index - while (sig(index) != '>') { - val tpname = subName(':'.==).toTypeName - val expname = if (owner.isClass) tpname.expandedName(owner) else tpname - val s = ctx.newSymbol( - owner, expname, owner.typeParamCreationFlags, - typeParamCompleter(index), coord = indexCoord(index)) - if (owner.isClass) owner.asClass.enter(s) - tparams = tparams + (tpname -> s) - sig2typeBounds(tparams, skiptvs = true) - newTParams += s - } - index += 1 - } - val ownTypeParams = newTParams.toList.asInstanceOf[List[TypeSymbol]] - val tpe = - if ((owner == null) || !owner.isClass) - sig2type(tparams, skiptvs = false) - else { - classTParams = tparams - val parents = new ListBuffer[Type]() - while (index < end) { - parents += sig2type(tparams, skiptvs = false) // here the variance doesnt'matter - } - TempClassInfoType(parents.toList, instanceScope, owner) - } - if (ownTypeParams.isEmpty) tpe else TempPolyType(ownTypeParams, tpe) - } // sigToType - - def parseAnnotArg(skip: Boolean = false)(implicit ctx: Context): Option[Tree] = { - val tag = in.nextByte.toChar - val index = in.nextChar - tag match { - case STRING_TAG => - if (skip) None else Some(Literal(Constant(pool.getName(index).toString))) - case BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG | INT_TAG | - LONG_TAG | FLOAT_TAG | DOUBLE_TAG => - if (skip) None else Some(Literal(pool.getConstant(index))) - case CLASS_TAG => - if (skip) None else Some(Literal(Constant(pool.getType(index)))) - case ENUM_TAG => - val t = pool.getType(index) - val n = pool.getName(in.nextChar) - val module = t.typeSymbol.companionModule - val s = module.info.decls.lookup(n) - if (skip) { - None - } else if (s != NoSymbol) { - Some(Literal(Constant(s))) - } else { - ctx.warning(s"""While parsing annotations in ${in.file}, could not find $n in enum $module.\nThis is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (SI-7014).""") - None - } - case ARRAY_TAG => - val arr = new ArrayBuffer[Tree]() - var hasError = false - for (i <- 0 until index) - parseAnnotArg(skip) match { - case Some(c) => arr += c - case None => hasError = true - } - if (hasError) None - else if (skip) None - else { - val elems = arr.toList - val elemType = - if (elems.isEmpty) defn.ObjectType - else ctx.typeComparer.lub(elems.tpes).widen - Some(JavaSeqLiteral(elems, TypeTree(elemType))) - } - case ANNOTATION_TAG => - parseAnnotation(index, skip) map (_.tree) - } - } - - /** Parse and return a single annotation. If it is malformed, - * return None. - */ - def parseAnnotation(attrNameIndex: Char, skip: Boolean = false)(implicit ctx: Context): Option[Annotation] = try { - val attrType = pool.getType(attrNameIndex) - val nargs = in.nextChar - val argbuf = new ListBuffer[Tree] - var hasError = false - for (i <- 0 until nargs) { - val name = pool.getName(in.nextChar) - parseAnnotArg(skip) match { - case Some(arg) => argbuf += NamedArg(name, arg) - case None => hasError = !skip - } - } - if (hasError || skip) None - else Some(Annotation.deferredResolve(attrType, argbuf.toList)) - } catch { - case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found - case NonFatal(ex) => - // We want to be robust when annotations are unavailable, so the very least - // we can do is warn the user about the exception - // There was a reference to ticket 1135, but that is outdated: a reference to a class not on - // the classpath would *not* end up here. A class not found is signaled - // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), - // and that should never be swallowed silently. - ctx.warning("Caught: " + ex + " while parsing annotations in " + in.file) - if (ctx.debug) ex.printStackTrace() - - None // ignore malformed annotations - } - - def parseAttributes(sym: Symbol, symtype: Type)(implicit ctx: Context): Type = { - def convertTo(c: Constant, pt: Type): Constant = { - if (pt == defn.BooleanType && c.tag == IntTag) - Constant(c.value != 0) - else - c convertTo pt - } - var newType = symtype - - def parseAttribute(): Unit = { - val attrName = pool.getName(in.nextChar).toTypeName - val attrLen = in.nextInt - val end = in.bp + attrLen - attrName match { - case tpnme.SignatureATTR => - val sig = pool.getExternalName(in.nextChar) - newType = sigToType(sig, sym) - if (ctx.debug && ctx.verbose) - println("" + sym + "; signature = " + sig + " type = " + newType) - case tpnme.SyntheticATTR => - sym.setFlag(Flags.SyntheticArtifact) - case tpnme.BridgeATTR => - sym.setFlag(Flags.Bridge) - case tpnme.DeprecatedATTR => - val msg = Literal(Constant("see corresponding Javadoc for more information.")) - val since = Literal(Constant("")) - sym.addAnnotation(Annotation(defn.DeprecatedAnnot, msg, since)) - case tpnme.ConstantValueATTR => - val c = pool.getConstant(in.nextChar) - val c1 = convertTo(c, symtype) - if (c1 ne null) newType = ConstantType(c1) - else println("failure to convert " + c + " to " + symtype); //debug - case tpnme.AnnotationDefaultATTR => - sym.addAnnotation(Annotation(defn.AnnotationDefaultAnnot, Nil)) - // Java annotations on classes / methods / fields with RetentionPolicy.RUNTIME - case tpnme.RuntimeAnnotationATTR => - parseAnnotations(attrLen) - - // TODO 1: parse runtime visible annotations on parameters - // case tpnme.RuntimeParamAnnotationATTR - - // TODO 2: also parse RuntimeInvisibleAnnotation / RuntimeInvisibleParamAnnotation, - // i.e. java annotations with RetentionPolicy.CLASS? - - case tpnme.ExceptionsATTR => - parseExceptions(attrLen) - - case tpnme.CodeATTR => - if (sym.owner is Flags.JavaTrait) { - sym.resetFlag(Flags.Deferred) - sym.owner.resetFlag(Flags.PureInterface) - ctx.log(s"$sym in ${sym.owner} is a java8+ default method.") - } - in.skip(attrLen) - - case _ => - } - in.bp = end - } - - /** - * Parse the "Exceptions" attribute which denotes the exceptions - * thrown by a method. - */ - def parseExceptions(len: Int): Unit = { - val nClasses = in.nextChar - for (n <- 0 until nClasses) { - // FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (SI-7065) - val cls = pool.getClassSymbol(in.nextChar.toInt) - sym.addAnnotation(ThrowsAnnotation(cls.asClass)) - } - } - - /** Parse a sequence of annotations and attaches them to the - * current symbol sym, except for the ScalaSignature annotation that it returns, if it is available. */ - def parseAnnotations(len: Int): Unit = { - val nAttr = in.nextChar - for (n <- 0 until nAttr) - parseAnnotation(in.nextChar) match { - case Some(annot) => - sym.addAnnotation(annot) - case None => - } - } - - // begin parseAttributes - for (i <- 0 until in.nextChar) { - parseAttribute() - } - newType - } - - /** Add synthetic constructor(s) and potentially also default getters which - * reflects the fields of the annotation with given `classInfo`. - * Annotations in Scala are assumed to get all their arguments as constructor - * parameters. For Java annotations we need to fake it by making up the constructor. - * Note that default getters have type Nothing. That's OK because we need - * them only to signal that the corresponding parameter is optional. - */ - def addAnnotationConstructor(classInfo: Type, tparams: List[TypeSymbol] = Nil)(implicit ctx: Context): Unit = { - def addDefaultGetter(attr: Symbol, n: Int) = - ctx.newSymbol( - owner = moduleRoot.symbol, - name = nme.CONSTRUCTOR.defaultGetterName(n), - flags = attr.flags & Flags.AccessFlags, - info = defn.NothingType).entered - - classInfo match { - case classInfo @ TempPolyType(tparams, restpe) if tparams.isEmpty => - addAnnotationConstructor(restpe, tparams) - case classInfo: TempClassInfoType => - val attrs = classInfo.decls.toList.filter(_.isTerm) - val targs = tparams.map(_.typeRef) - val paramNames = attrs.map(_.name.asTermName) - val paramTypes = attrs.map(_.info.resultType) - - def addConstr(ptypes: List[Type]) = { - val mtype = MethodType(paramNames, ptypes, classRoot.typeRef.appliedTo(targs)) - val constrType = if (tparams.isEmpty) mtype else TempPolyType(tparams, mtype) - val constr = ctx.newSymbol( - owner = classRoot.symbol, - name = nme.CONSTRUCTOR, - flags = Flags.Synthetic, - info = constrType - ).entered - for ((attr, i) <- attrs.zipWithIndex) - if (attr.hasAnnotation(defn.AnnotationDefaultAnnot)) { - constr.setFlag(Flags.HasDefaultParams) - addDefaultGetter(attr, i) - } - } - - addConstr(paramTypes) - - // The code below added an extra constructor to annotations where the - // last parameter of the constructor is an Array[X] for some X, the - // array was replaced by a vararg argument. Unfortunately this breaks - // inference when doing: - // @Annot(Array()) - // The constructor is overloaded so the expected type of `Array()` is - // WildcardType, and the type parameter of the Array apply method gets - // instantiated to `Nothing` instead of `X`. - // I'm leaving this commented out in case we improve inference to make this work. - // Note that if this is reenabled then JavaParser will also need to be modified - // to add the extra constructor (this was not implemented before). - /* - if (paramTypes.nonEmpty) - paramTypes.last match { - case defn.ArrayOf(elemtp) => - addConstr(paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp)) - case _ => - } - */ - } - } - - /** Enter own inner classes in the right scope. It needs the scopes to be set up, - * and implicitly current class' superclasses. - */ - private def enterOwnInnerClasses()(implicit ctx: Context): Unit = { - def className(name: Name): Name = name.drop(name.lastIndexOf('.') + 1) - - def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) = { - ctx.base.loaders.enterClassAndModule( - getOwner(jflags), - entry.originalName, - new ClassfileLoader(file), - classTranslation.flags(jflags), - getScope(jflags)) - } - - for (entry <- innerClasses.values) { - // create a new class member for immediate inner classes - if (entry.outerName == currentClassName) { - val file = ctx.platform.classPath.findSourceFile(entry.externalName.toString) getOrElse { - throw new AssertionError(entry.externalName) - } - enterClassAndModule(entry, file, entry.jflags) - } - } - } - - /** Parse inner classes. Expects `in.bp` to point to the superclass entry. - * Restores the old `bp`. - * @return true iff classfile is from Scala, so no Java info needs to be read. - */ - def unpickleOrParseInnerClasses()(implicit ctx: Context): Option[Embedded] = { - val oldbp = in.bp - try { - skipSuperclasses() - skipMembers() // fields - skipMembers() // methods - val attrs = in.nextChar - val attrbp = in.bp - - def scan(target: TypeName): Boolean = { - in.bp = attrbp - var i = 0 - while (i < attrs && pool.getName(in.nextChar).toTypeName != target) { - val attrLen = in.nextInt - in.skip(attrLen) - i += 1 - } - i < attrs - } - - def unpickleScala(bytes: Array[Byte]): Some[Embedded] = { - val unpickler = new unpickleScala2.Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx) - unpickler.run()(ctx.addMode(Mode.Scala2Unpickling)) - Some(unpickler) - } - - def unpickleTASTY(bytes: Array[Byte]): Some[Embedded] = { - val unpickler = new tasty.DottyUnpickler(bytes) - unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule)) - Some(unpickler) - } - - def parseScalaSigBytes: Array[Byte] = { - val tag = in.nextByte.toChar - assert(tag == STRING_TAG, tag) - pool getBytes in.nextChar - } - - def parseScalaLongSigBytes: Array[Byte] = { - val tag = in.nextByte.toChar - assert(tag == ARRAY_TAG, tag) - val stringCount = in.nextChar - val entries = - for (i <- 0 until stringCount) yield { - val stag = in.nextByte.toChar - assert(stag == STRING_TAG, stag) - in.nextChar.toInt - } - pool.getBytes(entries.toList) - } - - if (scan(tpnme.TASTYATTR)) { - val attrLen = in.nextInt - return unpickleTASTY(in.nextBytes(attrLen)) - } - - if (scan(tpnme.RuntimeAnnotationATTR)) { - val attrLen = in.nextInt - val nAnnots = in.nextChar - var i = 0 - while (i < nAnnots) { - val attrClass = pool.getType(in.nextChar).typeSymbol - val nArgs = in.nextChar - var j = 0 - while (j < nArgs) { - val argName = pool.getName(in.nextChar) - if (argName == nme.bytes) - if (attrClass == defn.ScalaSignatureAnnot) - return unpickleScala(parseScalaSigBytes) - else if (attrClass == defn.ScalaLongSignatureAnnot) - return unpickleScala(parseScalaLongSigBytes) - else if (attrClass == defn.TASTYSignatureAnnot) - return unpickleTASTY(parseScalaSigBytes) - else if (attrClass == defn.TASTYLongSignatureAnnot) - return unpickleTASTY(parseScalaLongSigBytes) - parseAnnotArg(skip = true) - j += 1 - } - i += 1 - } - } - - if (scan(tpnme.InnerClassesATTR)) { - val attrLen = in.nextInt - val entries = in.nextChar.toInt - for (i <- 0 until entries) { - val innerIndex = in.nextChar - val outerIndex = in.nextChar - val nameIndex = in.nextChar - val jflags = in.nextChar - if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) { - val entry = InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) - innerClasses(pool.getClassName(innerIndex)) = entry - } - } - } - None - } finally in.bp = oldbp - } - - /** An entry in the InnerClasses attribute of this class file. */ - case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: Int) { - def externalName = pool.getClassName(external) - def outerName = pool.getClassName(outer) - def originalName = pool.getName(name) - - override def toString = - originalName + " in " + outerName + "(" + externalName + ")" - } - - object innerClasses extends scala.collection.mutable.HashMap[Name, InnerClassEntry] { - /** Return the Symbol of the top level class enclosing `name`, - * or 'name's symbol if no entry found for `name`. - */ - def topLevelClass(name: Name)(implicit ctx: Context): Symbol = { - val tlName = if (isDefinedAt(name)) { - var entry = this(name) - while (isDefinedAt(entry.outerName)) - entry = this(entry.outerName) - entry.outerName - } else - name - classNameToSymbol(tlName) - } - - /** Return the class symbol for `externalName`. It looks it up in its outer class. - * Forces all outer class symbols to be completed. - * - * If the given name is not an inner class, it returns the symbol found in `defn`. - */ - def classSymbol(externalName: Name)(implicit ctx: Context): Symbol = { - /** Return the symbol of `innerName`, having the given `externalName`. */ - def innerSymbol(externalName: Name, innerName: Name, static: Boolean): Symbol = { - def getMember(sym: Symbol, name: Name): Symbol = - if (static) - if (sym == classRoot.symbol) staticScope.lookup(name) - else sym.companionModule.info.member(name).symbol - else - if (sym == classRoot.symbol) instanceScope.lookup(name) - else sym.info.member(name).symbol - - innerClasses.get(externalName) match { - case Some(entry) => - val outerName = entry.outerName.stripModuleClassSuffix - val owner = classSymbol(outerName) - val result = ctx.atPhaseNotLaterThanTyper { implicit ctx => - getMember(owner, innerName.toTypeName) - } - assert(result ne NoSymbol, - i"""failure to resolve inner class: - |externalName = $externalName, - |outerName = $outerName, - |innerName = $innerName - |owner.fullName = ${owner.showFullName} - |while parsing ${classfile}""") - result - - case None => - classNameToSymbol(externalName) - } - } - - get(externalName) match { - case Some(entry) => - innerSymbol(entry.externalName, entry.originalName, isStatic(entry.jflags)) - case None => - classNameToSymbol(externalName) - } - } - } - - def skipAttributes(): Unit = { - val attrCount = in.nextChar - for (i <- 0 until attrCount) { - in.skip(2); in.skip(in.nextInt) - } - } - - def skipMembers(): Unit = { - val memberCount = in.nextChar - for (i <- 0 until memberCount) { - in.skip(6); skipAttributes() - } - } - - def skipSuperclasses(): Unit = { - in.skip(2) // superclass - val ifaces = in.nextChar - in.skip(2 * ifaces) - } - - protected def getOwner(flags: Int): Symbol = - if (isStatic(flags)) moduleRoot.symbol else classRoot.symbol - - protected def getScope(flags: Int): MutableScope = - if (isStatic(flags)) staticScope else instanceScope - - private def setPrivateWithin(denot: SymDenotation, jflags: Int)(implicit ctx: Context): Unit = { - if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC)) == 0) - denot.privateWithin = denot.enclosingPackageClass - } - - private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0 - private def isStatic(flags: Int) = (flags & JAVA_ACC_STATIC) != 0 - private def hasAnnotation(flags: Int) = (flags & JAVA_ACC_ANNOTATION) != 0 - - class ConstantPool { - private val len = in.nextChar - private val starts = new Array[Int](len) - private val values = new Array[AnyRef](len) - private val internalized = new Array[TermName](len) - - { var i = 1 - while (i < starts.length) { - starts(i) = in.bp - i += 1 - (in.nextByte.toInt: @switch) match { - case CONSTANT_UTF8 | CONSTANT_UNICODE => - in.skip(in.nextChar) - case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE => - in.skip(2) - case CONSTANT_METHODHANDLE => - in.skip(3) - case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF - | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT - | CONSTANT_INVOKEDYNAMIC => - in.skip(4) - case CONSTANT_LONG | CONSTANT_DOUBLE => - in.skip(8) - i += 1 - case _ => - errorBadTag(in.bp - 1) - } - } - } - - /** Return the name found at given index. */ - def getName(index: Int): TermName = { - if (index <= 0 || len <= index) - errorBadIndex(index) - - values(index) match { - case name: TermName => name - case null => - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val name = termName(in.buf, start + 3, in.getChar(start + 1)) - values(index) = name - name - } - } - - /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ - def getExternalName(index: Int): TermName = { - if (index <= 0 || len <= index) - errorBadIndex(index) - - if (internalized(index) == null) - internalized(index) = getName(index).replace('/', '.') - - internalized(index) - } - - def getClassSymbol(index: Int)(implicit ctx: Context): Symbol = { - if (index <= 0 || len <= index) errorBadIndex(index) - var c = values(index).asInstanceOf[Symbol] - if (c eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - val name = getExternalName(in.getChar(start + 1)) - if (name.isModuleClassName && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass)) - // Null$ and Nothing$ ARE classes - c = ctx.requiredModule(name.sourceModuleName) - else c = classNameToSymbol(name) - values(index) = c - } - c - } - - /** Return the external name of the class info structure found at 'index'. - * Use 'getClassSymbol' if the class is sure to be a top-level class. - */ - def getClassName(index: Int): TermName = { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - getExternalName(in.getChar(start + 1)) - } - - /** Return a name and a type at the given index. - */ - private def getNameAndType(index: Int, ownerTpe: Type)(implicit ctx: Context): (Name, Type) = { - if (index <= 0 || len <= index) errorBadIndex(index) - var p = values(index).asInstanceOf[(Name, Type)] - if (p eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_NAMEANDTYPE) errorBadTag(start) - val name = getName(in.getChar(start + 1).toInt) - var tpe = getType(in.getChar(start + 3).toInt) - // fix the return type, which is blindly set to the class currently parsed - if (name == nme.CONSTRUCTOR) - tpe match { - case tp: MethodType => - tp.derivedMethodType(tp.paramNames, tp.paramTypes, ownerTpe) - } - p = (name, tpe) - values(index) = p - } - p - } - - /** Return the type of a class constant entry. Since - * arrays are considered to be class types, they might - * appear as entries in 'newarray' or 'cast' opcodes. - */ - def getClassOrArrayType(index: Int)(implicit ctx: Context): Type = { - if (index <= 0 || len <= index) errorBadIndex(index) - val value = values(index) - var c: Type = null - if (value eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - val name = getExternalName(in.getChar(start + 1)) - if (name(0) == ARRAY_TAG) { - c = sigToType(name) - values(index) = c - } else { - val sym = classNameToSymbol(name) - values(index) = sym - c = sym.typeRef - } - } else c = value match { - case tp: Type => tp - case cls: Symbol => cls.typeRef - } - c - } - - def getType(index: Int)(implicit ctx: Context): Type = - sigToType(getExternalName(index)) - - def getSuperClass(index: Int)(implicit ctx: Context): Symbol = { - assert(index != 0, "attempt to parse java.lang.Object from classfile") - getClassSymbol(index) - } - - def getConstant(index: Int)(implicit ctx: Context): Constant = { - if (index <= 0 || len <= index) errorBadIndex(index) - var value = values(index) - if (value eq null) { - val start = starts(index) - value = (in.buf(start).toInt: @switch) match { - case CONSTANT_STRING => - Constant(getName(in.getChar(start + 1).toInt).toString) - case CONSTANT_INTEGER => - Constant(in.getInt(start + 1)) - case CONSTANT_FLOAT => - Constant(in.getFloat(start + 1)) - case CONSTANT_LONG => - Constant(in.getLong(start + 1)) - case CONSTANT_DOUBLE => - Constant(in.getDouble(start + 1)) - case CONSTANT_CLASS => - getClassOrArrayType(index).typeSymbol - case _ => - errorBadTag(start) - } - values(index) = value - } - value match { - case ct: Constant => ct - case cls: Symbol => Constant(cls.typeRef) - case arr: Type => Constant(arr) - } - } - - private def getSubArray(bytes: Array[Byte]): Array[Byte] = { - val decodedLength = ByteCodecs.decode(bytes) - val arr = new Array[Byte](decodedLength) - System.arraycopy(bytes, 0, arr, 0, decodedLength) - arr - } - - def getBytes(index: Int): Array[Byte] = { - if (index <= 0 || len <= index) errorBadIndex(index) - var value = values(index).asInstanceOf[Array[Byte]] - if (value eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val len = in.getChar(start + 1) - val bytes = new Array[Byte](len) - System.arraycopy(in.buf, start + 3, bytes, 0, len) - value = getSubArray(bytes) - values(index) = value - } - value - } - - def getBytes(indices: List[Int]): Array[Byte] = { - assert(!indices.isEmpty, indices) - var value = values(indices.head).asInstanceOf[Array[Byte]] - if (value eq null) { - val bytesBuffer = ArrayBuffer.empty[Byte] - for (index <- indices) { - if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index) - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val len = in.getChar(start + 1) - bytesBuffer ++= in.buf.view(start + 3, start + 3 + len) - } - value = getSubArray(bytesBuffer.toArray) - values(indices.head) = value - } - value - } - - /** Throws an exception signaling a bad constant index. */ - private def errorBadIndex(index: Int) = - throw new RuntimeException("bad constant pool index: " + index + " at pos: " + in.bp) - - /** Throws an exception signaling a bad tag at given address. */ - private def errorBadTag(start: Int) = - throw new RuntimeException("bad constant pool tag " + in.buf(start) + " at byte " + start) - } -} - diff --git a/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala b/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala deleted file mode 100644 index 2c93819d5..000000000 --- a/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala +++ /dev/null @@ -1,53 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import Contexts._, SymDenotations._, Symbols._ -import dotty.tools.dotc.ast.tpd -import TastyUnpickler._, TastyBuffer._ -import util.Positions._ -import util.{SourceFile, NoSource} -import Annotations.Annotation -import core.Mode -import classfile.ClassfileParser - -object DottyUnpickler { - - /** Exception thrown if classfile is corrupted */ - class BadSignature(msg: String) extends RuntimeException(msg) - - class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler]) - extends SectionUnpickler[TreeUnpickler]("ASTs") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table) = - new TreeUnpickler(reader, tastyName, posUnpickler) - } - - class PositionsSectionUnpickler extends SectionUnpickler[PositionUnpickler]("Positions") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table) = - new PositionUnpickler(reader) - } -} - -/** A class for unpickling Tasty trees and symbols. - * @param bytes the bytearray containing the Tasty file from which we unpickle - */ -class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded { - import tpd._ - import DottyUnpickler._ - - val unpickler = new TastyUnpickler(bytes) - private val posUnpicklerOpt = unpickler.unpickle(new PositionsSectionUnpickler) - private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler(posUnpicklerOpt)).get - - /** Enter all toplevel classes and objects into their scopes - * @param roots a set of SymDenotations that should be overwritten by unpickling - */ - def enter(roots: Set[SymDenotation])(implicit ctx: Context): Unit = - treeUnpickler.enterTopLevel(roots) - - /** The unpickled trees, and the source file they come from. */ - def body(implicit ctx: Context): List[Tree] = { - treeUnpickler.unpickle() - } -} diff --git a/src/dotty/tools/dotc/core/tasty/NameBuffer.scala b/src/dotty/tools/dotc/core/tasty/NameBuffer.scala deleted file mode 100644 index 3ff7298ce..000000000 --- a/src/dotty/tools/dotc/core/tasty/NameBuffer.scala +++ /dev/null @@ -1,101 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import collection.mutable -import Names.{Name, chrs} -import Decorators._, NameOps._ -import TastyBuffer._ -import scala.io.Codec -import TastyName._ -import TastyFormat._ - -class NameBuffer extends TastyBuffer(10000) { - import NameBuffer._ - - private val nameRefs = new mutable.LinkedHashMap[TastyName, NameRef] - - def nameIndex(name: TastyName): NameRef = nameRefs.get(name) match { - case Some(ref) => - ref - case None => - val ref = NameRef(nameRefs.size) - nameRefs(name) = ref - ref - } - def nameIndex(name: Name): NameRef = { - val tname = - if (name.isShadowedName) Shadowed(nameIndex(name.revertShadowed)) - else Simple(name.toTermName) - nameIndex(tname) - } - - def nameIndex(str: String): NameRef = nameIndex(str.toTermName) - - def fullNameIndex(name: Name): NameRef = { - val pos = name.lastIndexOf('.') - if (pos > 0) - nameIndex(Qualified(fullNameIndex(name.take(pos)), nameIndex(name.drop(pos + 1)))) - else - nameIndex(name) - } - - private def withLength(op: => Unit, lengthWidth: Int = 1): Unit = { - val lengthAddr = currentAddr - for (i <- 0 until lengthWidth) writeByte(0) - op - val length = currentAddr.index - lengthAddr.index - 1 - putNat(lengthAddr, length, lengthWidth) - } - - def writeNameRef(ref: NameRef) = writeNat(ref.index) - - def pickleName(name: TastyName): Unit = name match { - case Simple(name) => - val bytes = - if (name.length == 0) new Array[Byte](0) - else Codec.toUTF8(chrs, name.start, name.length) - writeByte(UTF8) - writeNat(bytes.length) - writeBytes(bytes, bytes.length) - case Qualified(qualified, selector) => - writeByte(QUALIFIED) - withLength { writeNameRef(qualified); writeNameRef(selector) } - case Signed(original, params, result) => - writeByte(SIGNED) - withLength( - { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) }, - if ((params.length + 2) * maxIndexWidth <= maxNumInByte) 1 else 2) - case Expanded(prefix, original) => - writeByte(EXPANDED) - withLength { writeNameRef(prefix); writeNameRef(original) } - case ModuleClass(module) => - writeByte(OBJECTCLASS) - withLength { writeNameRef(module) } - case SuperAccessor(accessed) => - writeByte(SUPERACCESSOR) - withLength { writeNameRef(accessed) } - case DefaultGetter(method, paramNumber) => - writeByte(DEFAULTGETTER) - withLength { writeNameRef(method); writeNat(paramNumber) } - case Shadowed(original) => - writeByte(SHADOWED) - withLength { writeNameRef(original) } - } - - override def assemble(): Unit = { - var i = 0 - for ((name, ref) <- nameRefs) { - assert(ref.index == i) - i += 1 - pickleName(name) - } - } -} - -object NameBuffer { - private val maxIndexWidth = 3 // allows name indices up to 2^21. - private val payloadBitsPerByte = 7 // determined by nat encoding in TastyBuffer - private val maxNumInByte = (1 << payloadBitsPerByte) - 1 -} diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala deleted file mode 100644 index 546894a9e..000000000 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ /dev/null @@ -1,79 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import ast._ -import ast.Trees._ -import ast.Trees.WithLazyField -import TastyFormat._ -import core._ -import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._ -import collection.mutable -import TastyBuffer._ -import util.Positions._ - -class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr]) { - val buf = new TastyBuffer(5000) - pickler.newSection("Positions", buf) - import buf._ - import ast.tpd._ - - private val remainingAddrs = new java.util.IdentityHashMap[Tree, Iterator[Addr]] - - def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean, hasPoint: Boolean) = { - def toInt(b: Boolean) = if (b) 1 else 0 - (addrDelta << 3) | (toInt(hasStartDelta) << 2) | (toInt(hasEndDelta) << 1) | toInt(hasPoint) - } - - def picklePositions(roots: List[Tree])(implicit ctx: Context) = { - var lastIndex = 0 - var lastPos = Position(0, 0) - def pickleDeltas(index: Int, pos: Position) = { - val addrDelta = index - lastIndex - val startDelta = pos.start - lastPos.start - val endDelta = pos.end - lastPos.end - buf.writeInt(header(addrDelta, startDelta != 0, endDelta != 0, !pos.isSynthetic)) - if (startDelta != 0) buf.writeInt(startDelta) - if (endDelta != 0) buf.writeInt(endDelta) - if (!pos.isSynthetic) buf.writeInt(pos.pointDelta) - lastIndex = index - lastPos = pos - } - - /** True if x's position cannot be reconstructed automatically from its initialPos - */ - def alwaysNeedsPos(x: Positioned) = x match { - case _: WithLazyField[_] // initialPos is inaccurate for trees with lazy field - | _: Trees.PackageDef[_] => true // package defs might be split into several Tasty files - case _ => false - } - - def traverse(x: Any): Unit = x match { - case x: Tree @unchecked => - val pos = if (x.isInstanceOf[MemberDef]) x.pos else x.pos.toSynthetic - if (pos.exists && (pos != x.initialPos.toSynthetic || alwaysNeedsPos(x))) { - addrOfTree(x) match { - case Some(addr) => - //println(i"pickling $x with $pos at $addr") - pickleDeltas(addr.index, pos) - case _ => - //println(i"no address for $x") - } - } - //else if (x.pos.exists) println(i"skipping $x") - x match { - case x: MemberDef @unchecked => - for (ann <- x.symbol.annotations) traverse(ann.tree) - case _ => - } - traverse(x.productIterator) - case xs: TraversableOnce[_] => - xs.foreach(traverse) - case x: Annotation => - traverse(x.tree) - case _ => - } - traverse(roots) - } -} diff --git a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala deleted file mode 100644 index cbe213d89..000000000 --- a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala +++ /dev/null @@ -1,39 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - - -import util.Positions._ -import collection.mutable -import TastyBuffer.{Addr, NoAddr} - -/** Unpickler for tree positions */ -class PositionUnpickler(reader: TastyReader) { - import reader._ - - private[tasty] lazy val positions = { - val positions = new mutable.HashMap[Addr, Position] - var curIndex = 0 - var curStart = 0 - var curEnd = 0 - while (!isAtEnd) { - val header = readInt() - val addrDelta = header >> 3 - val hasStart = (header & 4) != 0 - val hasEnd = (header & 2) != 0 - val hasPoint = (header & 1) != 0 - curIndex += addrDelta - assert(curIndex >= 0) - if (hasStart) curStart += readInt() - if (hasEnd) curEnd += readInt() - positions(Addr(curIndex)) = - if (hasPoint) Position(curStart, curEnd, curStart + readInt()) - else Position(curStart, curEnd) - } - positions - } - - def posAt(addr: Addr) = positions.getOrElse(addr, NoPosition) -} - diff --git a/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala b/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala deleted file mode 100644 index 13bc95028..000000000 --- a/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala +++ /dev/null @@ -1,188 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import util.Util.dble - -object TastyBuffer { - - /** The number of digits of the natural number `nat`, written in base 128 format. */ - def natSize(nat: Int): Int = - if (nat < 128) 1 else natSize(nat >>> 7) + 1 - - /** An address pointing to an index in a Tasty buffer's byte array */ - case class Addr(index: Int) extends AnyVal { - def - (delta: Int): Addr = Addr(this.index - delta) - def + (delta: Int): Addr = Addr(this.index + delta) - - def relativeTo(base: Addr): Addr = this - base.index - AddrWidth - } - - val NoAddr = Addr(-1) - - /** The maximal number of address bytes. - * Since addresses are written as base-128 natural numbers, - * the value of 4 gives a maximal array size of 256M. - */ - final val AddrWidth = 4 -} -import TastyBuffer._ - -/** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, - * and that supports reading and patching addresses represented as natural numbers. - */ -class TastyBuffer(initialSize: Int) { - - /** The current byte array, will be expanded as needed */ - var bytes = new Array[Byte](initialSize) - - /** The number of bytes written */ - var length = 0 - - // -- Output routines -------------------------------------------- - - /** Write a byte of data. */ - def writeByte(b: Int): Unit = { - if (length >= bytes.length) - bytes = dble(bytes) - bytes(length) = b.toByte - length += 1 - } - - /** Write the first `n` bytes of `data`. */ - def writeBytes(data: Array[Byte], n: Int): Unit = { - while (bytes.length < length + n) bytes = dble(bytes) - Array.copy(data, 0, bytes, length, n) - length += n - } - - /** Write a natural number in big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def writeNat(x: Int): Unit = - writeLongNat(x.toLong & 0x00000000FFFFFFFFL) - - /** Write a natural number in 2's complement big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def writeInt(x: Int): Unit = - writeLongInt(x) - - /** - * Like writeNat, but for longs. Note that the - * binary representation of LongNat is identical to Nat - * if the long value is in the range Int.MIN_VALUE to - * Int.MAX_VALUE. - */ - def writeLongNat(x: Long): Unit = { - def writePrefix(x: Long): Unit = { - val y = x >>> 7 - if (y != 0L) writePrefix(y) - writeByte((x & 0x7f).toInt) - } - val y = x >>> 7 - if (y != 0L) writePrefix(y) - writeByte(((x & 0x7f) | 0x80).toInt) - } - - /** Like writeInt, but for longs */ - def writeLongInt(x: Long): Unit = { - def writePrefix(x: Long): Unit = { - val y = x >> 7 - if (y != 0L - ((x >> 6) & 1)) writePrefix(y) - writeByte((x & 0x7f).toInt) - } - val y = x >> 7 - if (y != 0L - ((x >> 6) & 1)) writePrefix(y) - writeByte(((x & 0x7f) | 0x80).toInt) - } - - /** Write an uncompressed Long stored in 8 bytes in big endian format */ - def writeUncompressedLong(x: Long): Unit = { - var y = x - val bytes = new Array[Byte](8) - for (i <- 7 to 0 by -1) { - bytes(i) = (y & 0xff).toByte - y = y >>> 8 - } - writeBytes(bytes, 8) - } - - // -- Address handling -------------------------------------------- - - /** Write natural number `x` right-adjusted in a field of `width` bytes - * starting with address `at`. - */ - def putNat(at: Addr, x: Int, width: Int): Unit = { - var y = x - var w = width - if(at.index + w >= bytes.length) - bytes = dble(bytes) - var digit = y & 0x7f | 0x80 - while (w > 0) { - w -= 1 - bytes(at.index + w) = digit.toByte - y >>>= 7 - digit = y & 0x7f - } - assert(y == 0, s"number $x too large to fit in $width bytes") - } - - /** The byte at given address */ - def getByte(at: Addr): Int = bytes(at.index) - - /** The natural number at address `at` */ - def getNat(at: Addr): Int = getLongNat(at).toInt - - /** The long natural number at address `at` */ - def getLongNat(at: Addr): Long = { - var b = 0L - var x = 0L - var idx = at.index - do { - b = bytes(idx) - x = (x << 7) | (b & 0x7f) - idx += 1 - } while ((b & 0x80) == 0) - x - } - - /** The address (represented as a natural number) at address `at` */ - def getAddr(at: Addr) = Addr(getNat(at)) - - /** The smallest address equal to or following `at` which points to a non-zero byte */ - final def skipZeroes(at: Addr): Addr = - if (getByte(at) != 0) at else skipZeroes(at + 1) - - /** The address after the natural number found at address `at`. */ - final def skipNat(at: Addr): Addr = { - val next = at + 1 - if ((getByte(at) & 0x80) != 0) next else skipNat(next) - } - - /** The address referring to the end of data written so far */ - def currentAddr: Addr = Addr(length) - - /** Reserve `AddrWidth` bytes to write an address into */ - def reserveAddr(): Addr = { - val result = currentAddr - length += AddrWidth - result - } - - /** Fill reserved space at address `at` with address `target` */ - def fillAddr(at: Addr, target: Addr) = - putNat(at, target.index, AddrWidth) - - /** Write address without leading zeroes */ - def writeAddr(addr: Addr): Unit = writeNat(addr.index) - - // -- Finalization -------------------------------------------- - - /** Hook to be overridden in subclasses. - * Perform all actions necessary to assemble the final byte array. - * After `assemble` no more output actions to this buffer are permitted. - */ - def assemble(): Unit = () -} diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala deleted file mode 100644 index cb1b56c3c..000000000 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ /dev/null @@ -1,553 +0,0 @@ -package dotty.tools.dotc -package core -package tasty - -/************************************************************ -Notation: - -We use BNF notation. Terminal symbols start with at least two -consecutive upper case letters. Each terminal is represented as a -single byte tag. Non-terminals are mixed case. Prefixes of the form -lower case letter*_ are for explanation of semantic content only, they -can be dropped without changing the grammar. - -Micro-syntax: - - LongInt = Digit* StopDigit // big endian 2's complement, value fits in a Long w/o overflow - Int = LongInt // big endian 2's complement, fits in an Int w/o overflow - Nat = LongInt // non-negative value, fits in an Int without overflow - Digit = 0 | ... | 127 - StopDigit = 128 | ... | 255 // value = digit - 128 - -Macro-format: - - File = Header majorVersion_Nat minorVersion_Nat UUID - nameTable_Length Name* Section* - Header = 0x5CA1AB1F - UUID = Byte*16 // random UUID - - Section = NameRef Length Bytes - Length = Nat // length of rest of entry in bytes - - Name = UTF8 Length UTF8-CodePoint* - QUALIFIED Length qualified_NameRef selector_NameRef - SIGNED Length original_NameRef resultSig_NameRef paramSig_NameRef* - EXPANDED Length original_NameRef - OBJECTCLASS Length module_NameRef - SUPERACCESSOR Length accessed_NameRef - DEFAULTGETTER Length method_NameRef paramNumber_Nat - SHADOWED Length original_NameRef - MANGLED Length mangle_NameRef name_NameRef - ... - - NameRef = Nat // ordinal number of name in name table, starting from 1. - -Note: Unqualified names in the name table are strings. The context decides whether a name is -a type-name or a term-name. The same string can represent both. - -Standard-Section: "ASTs" TopLevelStat* - - TopLevelStat = PACKAGE Length Path TopLevelStat* - Stat - - Stat = Term - VALDEF Length NameRef Type rhs_Term? Modifier* - DEFDEF Length NameRef TypeParam* Params* return_Type rhs_Term? - Modifier* - TYPEDEF Length NameRef (Type | Template) Modifier* - IMPORT Length qual_Term Selector* - Selector = IMPORTED name_NameRef - RENAMED to_NameRef - - // Imports are for scala.meta, they are not used in the backend - - TypeParam = TYPEPARAM Length NameRef Type Modifier* - Params = PARAMS Length Param* - Param = PARAM Length NameRef Type rhs_Term? Modifier* // rhs_Term is present in the case of an aliased class parameter - Template = TEMPLATE Length TypeParam* Param* Parent* Self? Stat* // Stat* always starts with the primary constructor. - Parent = Application - Type - Self = SELFDEF selfName_NameRef selfType_Type - - Term = Path - Application - IDENT NameRef Type // used when term ident’s type is not a TermRef - SELECT possiblySigned_NameRef qual_Term - QUALTHIS typeIdent_Tree - NEW cls_Type - SUPER Length this_Term mixinTypeIdent_Tree? - TYPED Length expr_Term ascription_Type - NAMEDARG Length paramName_NameRef arg_Term - ASSIGN Length lhs_Term rhs_Term - BLOCK Length expr_Term Stat* - INLINED Length call_Term expr_Term Stat* - LAMBDA Length meth_Term target_Type - IF Length cond_Term then_Term else_Term - MATCH Length sel_Term CaseDef* - TRY Length expr_Term CaseDef* finalizer_Term? - RETURN Length meth_ASTRef expr_Term? - REPEATED Length elem_Type elem_Term* - BIND Length boundName_NameRef patType_Type pat_Term - ALTERNATIVE Length alt_Term* - UNAPPLY Length fun_Term ImplicitArg* pat_Type pat_Term* - IDENTtpt NameRef Type // used for all type idents - SELECTtpt NameRef qual_Term - SINGLETONtpt Path - REFINEDtpt Length underlying_Term refinement_Stat* - APPLIEDtpt Length tycon_Term arg_Term* - POLYtpt Length TypeParam* body_Term - TYPEBOUNDStpt Length low_Term high_Term - ANNOTATEDtpt Length underlying_Term fullAnnotation_Term - ANDtpt Length left_Term right_Term - ORtpt Length left_Term right_Term - BYNAMEtpt underlying_Term - EMPTYTREE - SHARED term_ASTRef - Application = APPLY Length fn_Term arg_Term* - - TYPEAPPLY Length fn_Term arg_Type* - CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? - ImplicitArg = IMPLICITARG arg_Term - ASTRef = Nat // byte position in AST payload - - Path = Constant - TERMREFdirect sym_ASTRef - TERMREFsymbol sym_ASTRef qual_Type - TERMREFpkg fullyQualified_NameRef - TERMREF possiblySigned_NameRef qual_Type - THIS clsRef_Type - RECthis recType_ASTRef - SHARED path_ASTRef - - Constant = UNITconst - FALSEconst - TRUEconst - BYTEconst Int - SHORTconst Int - CHARconst Nat - INTconst Int - LONGconst LongInt - FLOATconst Int - DOUBLEconst LongInt - STRINGconst NameRef - NULLconst - CLASSconst Type - ENUMconst Path - - Type = Path - TYPEREFdirect sym_ASTRef - TYPEREFsymbol sym_ASTRef qual_Type - TYPEREFpkg fullyQualified_NameRef - TYPEREF possiblySigned_NameRef qual_Type - RECtype parent_Type - SUPERtype Length this_Type underlying_Type - REFINEDtype Length underlying_Type refinement_NameRef info_Type - APPLIEDtype Length tycon_Type arg_Type* - TYPEBOUNDS Length low_Type high_Type - TYPEALIAS Length alias_Type (COVARIANT | CONTRAVARIANT)? - ANNOTATEDtype Length underlying_Type fullAnnotation_Term - ANDtype Length left_Type right_Type - ORtype Length left_Type right_Type - BIND Length boundName_NameRef bounds_Type - // for type-variables defined in a type pattern - BYNAMEtype underlying_Type - POLYtype Length result_Type NamesTypes // variance encoded in front of name: +/-/= - METHODtype Length result_Type NamesTypes // needed for refinements - PARAMtype Length binder_ASTref paramNum_Nat // needed for refinements - SHARED type_ASTRef - NamesTypes = NameType* - NameType = paramName_NameRef typeOrBounds_ASTRef - - Modifier = PRIVATE - INTERNAL // package private - PROTECTED - PRIVATEqualified qualifier_Type // will be dropped - PROTECTEDqualified qualifier_Type // will be dropped - ABSTRACT - FINAL - SEALED - CASE - IMPLICIT - LAZY - OVERRIDE - INLINE // macro - STATIC // mapped to static Java member - OBJECT // an object or its class - TRAIT // a trait - LOCAL // private[this] or protected[this] - SYNTHETIC // generated by Scala compiler - ARTIFACT // to be tagged Java Synthetic - MUTABLE // a var - LABEL // method generated as a label - FIELDaccessor // getter or setter - CASEaccessor // getter for case class param - COVARIANT // type param marked “+” - CONTRAVARIANT // type param marked “-” - SCALA2X // Imported from Scala2.x - DEFAULTparameterized // Method with default params - INSUPERCALL // defined in the argument of a constructor supercall - STABLE // Method that is assumed to be stable - Annotation - Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term - -Note: Tree tags are grouped into 5 categories that determine what follows, and thus allow to compute the size of the tagged tree in a generic way. - - Category 1 (tags 0-63) : tag - Category 2 (tags 64-95) : tag Nat - Category 3 (tags 96-111) : tag AST - Category 4 (tags 112-127): tag Nat AST - Category 5 (tags 128-255): tag Length <payload> - -Standard Section: "Positions" Assoc* - - Assoc = Header offset_Delta? offset_Delta? - Header = addr_Delta + // in one Nat: difference of address to last recorded node << 2 + - hasStartDiff + // one bit indicating whether there follows a start address delta << 1 - hasEndDiff // one bit indicating whether there follows an end address delta - // Nodes which have the same positions as their parents are omitted. - // offset_Deltas give difference of start/end offset wrt to the - // same offset in the previously recorded node (or 0 for the first recorded node) - Delta = Int // Difference between consecutive offsets, - -**************************************************************************************/ - -object TastyFormat { - - final val header = Array(0x5C, 0xA1, 0xAB, 0x1F) - final val MajorVersion = 0 - final val MinorVersion = 5 - - // Name tags - - final val UTF8 = 1 - final val QUALIFIED = 2 - final val SIGNED = 3 - final val EXPANDED = 4 - final val OBJECTCLASS = 5 - final val SUPERACCESSOR = 6 - final val DEFAULTGETTER = 7 - final val SHADOWED = 8 - - // AST tags - - final val UNITconst = 2 - final val FALSEconst = 3 - final val TRUEconst = 4 - final val NULLconst = 5 - final val PRIVATE = 6 - final val INTERNAL = 7 - final val PROTECTED = 8 - final val ABSTRACT = 9 - final val FINAL = 10 - final val SEALED = 11 - final val CASE = 12 - final val IMPLICIT = 13 - final val LAZY = 14 - final val OVERRIDE = 15 - final val INLINE = 16 - final val STATIC = 17 - final val OBJECT = 18 - final val TRAIT = 19 - final val LOCAL = 20 - final val SYNTHETIC = 21 - final val ARTIFACT = 22 - final val MUTABLE = 23 - final val LABEL = 24 - final val FIELDaccessor = 25 - final val CASEaccessor = 26 - final val COVARIANT = 27 - final val CONTRAVARIANT = 28 - final val SCALA2X = 29 - final val DEFAULTparameterized = 30 - final val INSUPERCALL = 31 - final val STABLE = 32 - - final val SHARED = 64 - final val TERMREFdirect = 65 - final val TYPEREFdirect = 66 - final val TERMREFpkg = 67 - final val TYPEREFpkg = 68 - final val RECthis = 69 - final val BYTEconst = 70 - final val SHORTconst = 71 - final val CHARconst = 72 - final val INTconst = 73 - final val LONGconst = 74 - final val FLOATconst = 75 - final val DOUBLEconst = 76 - final val STRINGconst = 77 - final val IMPORTED = 78 - final val RENAMED = 79 - - final val THIS = 96 - final val QUALTHIS = 97 - final val CLASSconst = 98 - final val ENUMconst = 99 - final val BYNAMEtype = 100 - final val BYNAMEtpt = 101 - final val NEW = 102 - final val IMPLICITarg = 103 - final val PRIVATEqualified = 104 - final val PROTECTEDqualified = 105 - final val RECtype = 106 - final val SINGLETONtpt = 107 - - final val IDENT = 112 - final val IDENTtpt = 113 - final val SELECT = 114 - final val SELECTtpt = 115 - final val TERMREFsymbol = 116 - final val TERMREF = 117 - final val TYPEREFsymbol = 118 - final val TYPEREF = 119 - final val SELFDEF = 120 - - final val PACKAGE = 128 - final val VALDEF = 129 - final val DEFDEF = 130 - final val TYPEDEF = 131 - final val IMPORT = 132 - final val TYPEPARAM = 133 - final val PARAMS = 134 - final val PARAM = 136 - final val APPLY = 137 - final val TYPEAPPLY = 138 - final val TYPED = 139 - final val NAMEDARG = 140 - final val ASSIGN = 141 - final val BLOCK = 142 - final val IF = 143 - final val LAMBDA = 144 - final val MATCH = 145 - final val RETURN = 146 - final val TRY = 147 - final val INLINED = 148 - final val REPEATED = 149 - final val BIND = 150 - final val ALTERNATIVE = 151 - final val UNAPPLY = 152 - final val ANNOTATEDtype = 153 - final val ANNOTATEDtpt = 154 - final val CASEDEF = 155 - final val TEMPLATE = 156 - final val SUPER = 157 - final val SUPERtype = 158 - final val REFINEDtype = 159 - final val REFINEDtpt = 160 - final val APPLIEDtype = 161 - final val APPLIEDtpt = 162 - final val TYPEBOUNDS = 163 - final val TYPEBOUNDStpt = 164 - final val TYPEALIAS = 165 - final val ANDtype = 166 - final val ANDtpt = 167 - final val ORtype = 168 - final val ORtpt = 169 - final val METHODtype = 170 - final val POLYtype = 171 - final val POLYtpt = 172 - final val PARAMtype = 173 - final val ANNOTATION = 174 - - final val firstSimpleTreeTag = UNITconst - final val firstNatTreeTag = SHARED - final val firstASTTreeTag = THIS - final val firstNatASTTreeTag = IDENT - final val firstLengthTreeTag = PACKAGE - - def isParamTag(tag: Int) = tag == PARAM || tag == TYPEPARAM - - def isModifierTag(tag: Int) = tag match { - case PRIVATE - | INTERNAL - | PROTECTED - | ABSTRACT - | FINAL - | SEALED - | CASE - | IMPLICIT - | LAZY - | OVERRIDE - | INLINE - | STATIC - | OBJECT - | TRAIT - | LOCAL - | SYNTHETIC - | ARTIFACT - | MUTABLE - | LABEL - | FIELDaccessor - | CASEaccessor - | COVARIANT - | CONTRAVARIANT - | SCALA2X - | DEFAULTparameterized - | INSUPERCALL - | STABLE - | ANNOTATION - | PRIVATEqualified - | PROTECTEDqualified => true - case _ => false - } - - def isTypeTreeTag(tag: Int) = tag match { - case IDENTtpt - | SELECTtpt - | SINGLETONtpt - | REFINEDtpt - | APPLIEDtpt - | POLYtpt - | TYPEBOUNDStpt - | ANNOTATEDtpt - | ANDtpt - | ORtpt - | BYNAMEtpt => true - case _ => false - } - - def nameTagToString(tag: Int): String = tag match { - case UTF8 => "UTF8" - case QUALIFIED => "QUALIFIED" - case SIGNED => "SIGNED" - case EXPANDED => "EXPANDED" - case OBJECTCLASS => "OBJECTCLASS" - case SUPERACCESSOR => "SUPERACCESSOR" - case DEFAULTGETTER => "DEFAULTGETTER" - } - - def astTagToString(tag: Int): String = tag match { - case UNITconst => "UNITconst" - case FALSEconst => "FALSEconst" - case TRUEconst => "TRUEconst" - case NULLconst => "NULLconst" - case PRIVATE => "PRIVATE" - case INTERNAL => "INTERNAL" - case PROTECTED => "PROTECTED" - case ABSTRACT => "ABSTRACT" - case FINAL => "FINAL" - case SEALED => "SEALED" - case CASE => "CASE" - case IMPLICIT => "IMPLICIT" - case LAZY => "LAZY" - case OVERRIDE => "OVERRIDE" - case INLINE => "INLINE" - case STATIC => "STATIC" - case OBJECT => "OBJECT" - case TRAIT => "TRAIT" - case LOCAL => "LOCAL" - case SYNTHETIC => "SYNTHETIC" - case ARTIFACT => "ARTIFACT" - case MUTABLE => "MUTABLE" - case LABEL => "LABEL" - case FIELDaccessor => "FIELDaccessor" - case CASEaccessor => "CASEaccessor" - case COVARIANT => "COVARIANT" - case CONTRAVARIANT => "CONTRAVARIANT" - case SCALA2X => "SCALA2X" - case DEFAULTparameterized => "DEFAULTparameterized" - case INSUPERCALL => "INSUPERCALL" - case STABLE => "STABLE" - - case SHARED => "SHARED" - case TERMREFdirect => "TERMREFdirect" - case TYPEREFdirect => "TYPEREFdirect" - case TERMREFpkg => "TERMREFpkg" - case TYPEREFpkg => "TYPEREFpkg" - case RECthis => "RECthis" - case BYTEconst => "BYTEconst" - case SHORTconst => "SHORTconst" - case CHARconst => "CHARconst" - case INTconst => "INTconst" - case LONGconst => "LONGconst" - case FLOATconst => "FLOATconst" - case DOUBLEconst => "DOUBLEconst" - case STRINGconst => "STRINGconst" - case RECtype => "RECtype" - - case IDENT => "IDENT" - case IDENTtpt => "IDENTtpt" - case SELECT => "SELECT" - case SELECTtpt => "SELECTtpt" - case TERMREFsymbol => "TERMREFsymbol" - case TERMREF => "TERMREF" - case TYPEREFsymbol => "TYPEREFsymbol" - case TYPEREF => "TYPEREF" - - case PACKAGE => "PACKAGE" - case VALDEF => "VALDEF" - case DEFDEF => "DEFDEF" - case TYPEDEF => "TYPEDEF" - case IMPORT => "IMPORT" - case TYPEPARAM => "TYPEPARAM" - case PARAMS => "PARAMS" - case PARAM => "PARAM" - case IMPORTED => "IMPORTED" - case RENAMED => "RENAMED" - case APPLY => "APPLY" - case TYPEAPPLY => "TYPEAPPLY" - case NEW => "NEW" - case TYPED => "TYPED" - case NAMEDARG => "NAMEDARG" - case ASSIGN => "ASSIGN" - case BLOCK => "BLOCK" - case IF => "IF" - case LAMBDA => "LAMBDA" - case MATCH => "MATCH" - case RETURN => "RETURN" - case INLINED => "INLINED" - case TRY => "TRY" - case REPEATED => "REPEATED" - case BIND => "BIND" - case ALTERNATIVE => "ALTERNATIVE" - case UNAPPLY => "UNAPPLY" - case ANNOTATEDtype => "ANNOTATEDtype" - case ANNOTATEDtpt => "ANNOTATEDtpt" - case CASEDEF => "CASEDEF" - case IMPLICITarg => "IMPLICITarg" - case TEMPLATE => "TEMPLATE" - case SELFDEF => "SELFDEF" - case THIS => "THIS" - case QUALTHIS => "QUALTHIS" - case SUPER => "SUPER" - case CLASSconst => "CLASSconst" - case ENUMconst => "ENUMconst" - case SINGLETONtpt => "SINGLETONtpt" - case SUPERtype => "SUPERtype" - case REFINEDtype => "REFINEDtype" - case REFINEDtpt => "REFINEDtpt" - case APPLIEDtype => "APPLIEDtype" - case APPLIEDtpt => "APPLIEDtpt" - case TYPEBOUNDS => "TYPEBOUNDS" - case TYPEBOUNDStpt => "TYPEBOUNDStpt" - case TYPEALIAS => "TYPEALIAS" - case ANDtype => "ANDtype" - case ANDtpt => "ANDtpt" - case ORtype => "ORtype" - case ORtpt => "ORtpt" - case BYNAMEtype => "BYNAMEtype" - case BYNAMEtpt => "BYNAMEtpt" - case POLYtype => "POLYtype" - case POLYtpt => "POLYtpt" - case METHODtype => "METHODtype" - case PARAMtype => "PARAMtype" - case ANNOTATION => "ANNOTATION" - case PRIVATEqualified => "PRIVATEqualified" - case PROTECTEDqualified => "PROTECTEDqualified" - } - - /** @return If non-negative, the number of leading references (represented as nats) of a length/trees entry. - * If negative, minus the number of leading non-reference trees. - */ - def numRefs(tag: Int) = tag match { - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | - SELFDEF | REFINEDtype => 1 - case RENAMED | PARAMtype => 2 - case POLYtype | METHODtype => -1 - case _ => 0 - } - - /** Map between variances and name prefixes */ - val varianceToPrefix = Map(-1 -> '-', 0 -> '=', 1 -> '+') - val prefixToVariance = Map('-' -> -1, '=' -> 0, '+' -> 1) -} diff --git a/src/dotty/tools/dotc/core/tasty/TastyName.scala b/src/dotty/tools/dotc/core/tasty/TastyName.scala deleted file mode 100644 index 26807115c..000000000 --- a/src/dotty/tools/dotc/core/tasty/TastyName.scala +++ /dev/null @@ -1,30 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import core.Names.TermName -import collection.mutable - -abstract class TastyName - -object TastyName { - - case class NameRef(index: Int) extends AnyVal - - case class Simple(name: TermName) extends TastyName - case class Qualified(qualified: NameRef, selector: NameRef) extends TastyName - case class Signed(original: NameRef, params: List[NameRef], result: NameRef) extends TastyName - case class Expanded(prefix: NameRef, original: NameRef) extends TastyName - case class ModuleClass(module: NameRef) extends TastyName - case class SuperAccessor(accessed: NameRef) extends TastyName - case class DefaultGetter(method: NameRef, num: Int) extends TastyName - case class Shadowed(original: NameRef) extends TastyName - - class Table extends (NameRef => TastyName) { - private val names = new mutable.ArrayBuffer[TastyName] - def add(name: TastyName) = names += name - def apply(ref: NameRef) = names(ref.index) - def contents: Iterable[TastyName] = names - } -} diff --git a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala deleted file mode 100644 index c844d522e..000000000 --- a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala +++ /dev/null @@ -1,71 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import TastyFormat._ -import collection.mutable -import TastyBuffer._ -import java.util.UUID -import core.Symbols.Symbol -import ast.tpd - -class TastyPickler { - - private val sections = new mutable.ArrayBuffer[(TastyName.NameRef, TastyBuffer)] - val uuid = UUID.randomUUID() - - private val headerBuffer = { - val buf = new TastyBuffer(24) - for (ch <- header) buf.writeByte(ch.toByte) - buf.writeNat(MajorVersion) - buf.writeNat(MinorVersion) - buf.writeUncompressedLong(uuid.getMostSignificantBits) - buf.writeUncompressedLong(uuid.getLeastSignificantBits) - buf - } - - val nameBuffer = new NameBuffer - - def newSection(name: String, buf: TastyBuffer) = - sections += ((nameBuffer.nameIndex(name), buf)) - - def assembleParts(): Array[Byte] = { - def lengthWithLength(buf: TastyBuffer) = { - buf.assemble() - buf.length + natSize(buf.length) - } - val totalSize = - headerBuffer.length + - lengthWithLength(nameBuffer) + { - for ((nameRef, buf) <- sections) yield - natSize(nameRef.index) + lengthWithLength(buf) - }.sum - val all = new TastyBuffer(totalSize) - all.writeBytes(headerBuffer.bytes, headerBuffer.length) - all.writeNat(nameBuffer.length) - all.writeBytes(nameBuffer.bytes, nameBuffer.length) - for ((nameRef, buf) <- sections) { - all.writeNat(nameRef.index) - all.writeNat(buf.length) - all.writeBytes(buf.bytes, buf.length) - } - assert(all.length == totalSize && all.bytes.length == totalSize, s"totalSize = $totalSize, all.length = ${all.length}, all.bytes.length = ${all.bytes.length}") - all.bytes - } - - /** The address in the TASTY file of a given tree, or None if unknown. - * Note that trees are looked up by reference equality, - * so one can reliably use this function only directly after `pickler`. - */ - var addrOfTree: tpd.Tree => Option[Addr] = (_ => None) - - /** - * Addresses in TASTY file of symbols, stored by pickling. - * Note that trees are checked for reference equality, - * so one can reliably use this function only dirrectly after `pickler` - */ - var addrOfSym: Symbol => Option[Addr] = (_ => None) - - val treePkl = new TreePickler(this) -} diff --git a/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala deleted file mode 100644 index 0dc8d8fea..000000000 --- a/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ /dev/null @@ -1,122 +0,0 @@ -package dotty.tools.dotc -package core -package tasty - -import Contexts._, Decorators._ -import printing.Texts._ -import TastyName._ -import StdNames._ -import TastyUnpickler._ -import TastyBuffer.Addr -import util.Positions.{Position, offsetToInt} -import collection.mutable - -class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { - - val unpickler = new TastyUnpickler(bytes) - import unpickler.{tastyName, unpickle} - - def nameToString(name: TastyName): String = name match { - case Simple(name) => name.toString - case Qualified(qual, name) => nameRefToString(qual) + "." + nameRefToString(name) - case Signed(original, params, result) => - i"${nameRefToString(original)}@${params.map(nameRefToString)}%,%:${nameRefToString(result)}" - case Expanded(prefix, original) => s"$prefix${nme.EXPAND_SEPARATOR}$original" - case ModuleClass(original) => nameRefToString(original) + "/MODULECLASS" - case SuperAccessor(accessed) => nameRefToString(accessed) + "/SUPERACCESSOR" - case DefaultGetter(meth, num) => nameRefToString(meth) + "/DEFAULTGETTER" + num - case Shadowed(original) => nameRefToString(original) + "/SHADOWED" - } - - def nameRefToString(ref: NameRef): String = nameToString(tastyName(ref)) - - def printNames() = - for ((name, idx) <- tastyName.contents.zipWithIndex) - println(f"$idx%4d: " + nameToString(name)) - - def printContents(): Unit = { - println("Names:") - printNames() - println("Trees:") - unpickle(new TreeSectionUnpickler) - unpickle(new PositionSectionUnpickler) - } - - class TreeSectionUnpickler extends SectionUnpickler[Unit]("ASTs") { - import TastyFormat._ - def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { - import reader._ - var indent = 0 - def newLine() = print(f"\n ${index(currentAddr) - index(startAddr)}%5d:" + " " * indent) - def printNat() = print(" " + readNat()) - def printName() = { - val idx = readNat() - print(" ") ;print(idx); print("["); print(nameRefToString(NameRef(idx))); print("]") - } - def printTree(): Unit = { - newLine() - val tag = readByte() - print(" ");print(astTagToString(tag)) - indent += 2 - if (tag >= firstLengthTreeTag) { - val len = readNat() - print(s"($len)") - val end = currentAddr + len - def printTrees() = until(end)(printTree()) - tag match { - case RENAMED => - printName(); printName() - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | BIND => - printName(); printTrees() - case REFINEDtype => - printName(); printTree(); printTrees() - case RETURN => - printNat(); printTrees() - case METHODtype | POLYtype => - printTree() - until(end) { printName(); printTree() } - case PARAMtype => - printNat(); printNat() - case _ => - printTrees() - } - if (currentAddr != end) { - println(s"incomplete read, current = $currentAddr, end = $end") - goto(end) - } - } - else if (tag >= firstNatASTTreeTag) { - tag match { - case IDENT | SELECT | TERMREF | TYPEREF | SELFDEF => printName() - case _ => printNat() - } - printTree() - } - else if (tag >= firstASTTreeTag) - printTree() - else if (tag >= firstNatTreeTag) - tag match { - case TERMREFpkg | TYPEREFpkg | STRINGconst | IMPORTED => printName() - case _ => printNat() - } - indent -= 2 - } - println(i"start = ${reader.startAddr}, base = $base, current = $currentAddr, end = $endAddr") - println(s"${endAddr.index - startAddr.index} bytes of AST, base = $currentAddr") - while (!isAtEnd) { - printTree() - newLine() - } - } - } - - class PositionSectionUnpickler extends SectionUnpickler[Unit]("Positions") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { - print(s"${reader.endAddr.index - reader.currentAddr.index}") - val positions = new PositionUnpickler(reader).positions - println(s" position bytes:") - val sorted = positions.toSeq.sortBy(_._1.index) - for ((addr, pos) <- sorted) println(s"${addr.index}: ${offsetToInt(pos.start)} .. ${pos.end}") - } - } -} diff --git a/src/dotty/tools/dotc/core/tasty/TastyReader.scala b/src/dotty/tools/dotc/core/tasty/TastyReader.scala deleted file mode 100644 index e583c4793..000000000 --- a/src/dotty/tools/dotc/core/tasty/TastyReader.scala +++ /dev/null @@ -1,141 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import TastyBuffer._ -import TastyName.NameRef -import collection.mutable - -/** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, - * and that supports reading and patching addresses represented as natural numbers. - * - * @param bytes The array containing data - * @param start The position from which to read - * @param end The position one greater than the last byte to be read - * @param base The index referenced by the logical zero address Addr(0) - */ -class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int = 0) { - - def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length) - - private var bp: Int = start - - def addr(idx: Int) = Addr(idx - base) - def index(addr: Addr) = addr.index + base - - /** The address of the first byte to read, respectively byte that was read */ - def startAddr: Addr = addr(start) - - /** The address of the next byte to read */ - def currentAddr: Addr = addr(bp) - - /** the address one greater than the last brte to read */ - def endAddr: Addr = addr(end) - - /** Have all bytes been read? */ - def isAtEnd: Boolean = bp == end - - /** A new reader over the same array with the same address base, but with - * specified start and end positions - */ - def subReader(start: Addr, end: Addr): TastyReader = - new TastyReader(bytes, index(start), index(end), base) - - /** Read a byte of data. */ - def readByte(): Int = { - val result = bytes(bp) & 0xff - bp += 1 - result - } - - /** Returns the next byte of data as a natural number without advancing the read position */ - def nextByte: Int = bytes(bp) & 0xff - - /** Read the next `n` bytes of `data`. */ - def readBytes(n: Int): Array[Byte] = { - val result = new Array[Byte](n) - Array.copy(bytes, bp, result, 0, n) - bp += n - result - } - - /** Read a natural number fitting in an Int in big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def readNat(): Int = readLongNat.toInt - - /** Read an integer number in 2's complement big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def readInt(): Int = readLongInt.toInt - - /** Read a natural number fitting in a Long in big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def readLongNat(): Long = { - var b = 0L - var x = 0L - do { - b = bytes(bp) - x = (x << 7) | (b & 0x7f) - bp += 1 - } while ((b & 0x80) == 0) - x - } - - /** Read a long integer number in 2's complement big endian format, base 128. */ - def readLongInt(): Long = { - var b = bytes(bp) - var x: Long = (b << 1).toByte >> 1 // sign extend with bit 6. - bp += 1 - while ((b & 0x80) == 0) { - b = bytes(bp) - x = (x << 7) | (b & 0x7f) - bp += 1 - } - x - } - - /** Read an uncompressed Long stored in 8 bytes in big endian format */ - def readUncompressedLong(): Long = { - var x: Long = 0 - for (i <- 0 to 7) - x = (x << 8) | (readByte() & 0xff) - x - } - - /** Read a natural number and return as a NameRef */ - def readNameRef() = NameRef(readNat()) - - /** Read a natural number and return as an address */ - def readAddr() = Addr(readNat()) - - /** Read a length number and return the absolute end address implied by it, - * given as <address following length field> + <length-value-read>. - */ - def readEnd(): Addr = addr(readNat() + bp) - - /** Set read position to the one pointed to by `addr` */ - def goto(addr: Addr): Unit = - bp = index(addr) - - /** Perform `op` until `end` address is reached and collect results in a list. */ - def until[T](end: Addr)(op: => T): List[T] = { - val buf = new mutable.ListBuffer[T] - while (bp < index(end)) buf += op - assert(bp == index(end)) - buf.toList - } - - /** If before given `end` address, the result of `op`, otherwise `default` */ - def ifBefore[T](end: Addr)(op: => T, default: T): T = - if (bp < index(end)) op else default - - /** Perform `op` while cindition `cond` holds and collect results in a list. */ - def collectWhile[T](cond: => Boolean)(op: => T): List[T] = { - val buf = new mutable.ListBuffer[T] - while (cond) buf += op - buf.toList - } -} diff --git a/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala deleted file mode 100644 index 8a1f58acd..000000000 --- a/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ /dev/null @@ -1,95 +0,0 @@ -package dotty.tools.dotc -package core -package tasty - -import scala.collection.mutable -import TastyFormat._ -import Names.{Name, termName} -import java.util.UUID - -object TastyUnpickler { - class UnpickleException(msg: String) extends Exception(msg) - - abstract class SectionUnpickler[R](val name: String) { - def unpickle(reader: TastyReader, tastyName: TastyName.Table): R - } -} - -import TastyUnpickler._ - -class TastyUnpickler(reader: TastyReader) { - import reader._ - - def this(bytes: Array[Byte]) = this(new TastyReader(bytes)) - - private val sectionReader = new mutable.HashMap[String, TastyReader] - val tastyName = new TastyName.Table - - def check(cond: Boolean, msg: => String) = - if (!cond) throw new UnpickleException(msg) - - def readString(): String = { - val TastyName.Simple(name) = tastyName(readNameRef()) - name.toString - } - - def readName(): TastyName = { - import TastyName._ - val tag = readByte() - val length = readNat() - val start = currentAddr - val end = start + length - val result = tag match { - case UTF8 => - goto(end) - Simple(termName(bytes, start.index, length)) - case QUALIFIED => - Qualified(readNameRef(), readNameRef()) - case SIGNED => - val original = readNameRef() - val result = readNameRef() - val params = until(end)(readNameRef()) - Signed(original, params, result) - case EXPANDED => - Expanded(readNameRef(), readNameRef()) - case OBJECTCLASS => - ModuleClass(readNameRef()) - case SUPERACCESSOR => - SuperAccessor(readNameRef()) - case DEFAULTGETTER => - DefaultGetter(readNameRef(), readNat()) - case SHADOWED => - Shadowed(readNameRef()) - } - assert(currentAddr == end, s"bad name $result $start $currentAddr $end") - result - } - - private def readHeader(): UUID = { - for (i <- 0 until header.length) - check(readByte() == header(i), "not a TASTy file") - val major = readNat() - val minor = readNat() - check(major == MajorVersion && minor <= MinorVersion, - s"""TASTy signature has wrong version. - | expected: $MajorVersion.$MinorVersion - | found : $major.$minor""".stripMargin) - new UUID(readUncompressedLong(), readUncompressedLong()) - } - - val uuid = readHeader() - - locally { - until(readEnd()) { tastyName.add(readName()) } - while (!isAtEnd) { - val secName = readString() - val secEnd = readEnd() - sectionReader(secName) = new TastyReader(bytes, currentAddr.index, secEnd.index, currentAddr.index) - goto(secEnd) - } - } - - def unpickle[R](sec: SectionUnpickler[R]): Option[R] = - for (reader <- sectionReader.get(sec.name)) yield - sec.unpickle(reader, tastyName) -} diff --git a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala deleted file mode 100644 index 6c7982d78..000000000 --- a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala +++ /dev/null @@ -1,188 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import util.Util.{bestFit, dble} -import TastyBuffer.{Addr, AddrWidth} -import config.Printers.pickling -import ast.untpd.Tree - -class TreeBuffer extends TastyBuffer(50000) { - - private final val ItemsOverOffsets = 2 - private val initialOffsetSize = bytes.length / (AddrWidth * ItemsOverOffsets) - private var offsets = new Array[Int](initialOffsetSize) - private var isRelative = new Array[Boolean](initialOffsetSize) - private var delta: Array[Int] = _ - private var numOffsets = 0 - - /** A map from trees to the address at which a tree is pickled. */ - private val treeAddrs = new java.util.IdentityHashMap[Tree, Any] // really: Addr | Null - - def registerTreeAddr(tree: Tree): Addr = treeAddrs.get(tree) match { - case null => treeAddrs.put(tree, currentAddr); currentAddr - case addr: Addr => addr - } - - def addrOfTree(tree: Tree): Option[Addr] = treeAddrs.get(tree) match { - case null => None - case addr: Addr => Some(addr) - } - - private def offset(i: Int): Addr = Addr(offsets(i)) - - private def keepOffset(relative: Boolean): Unit = { - if (numOffsets == offsets.length) { - offsets = dble(offsets) - isRelative = dble(isRelative) - } - offsets(numOffsets) = length - isRelative(numOffsets) = relative - numOffsets += 1 - } - - /** Reserve space for a reference, to be adjusted later */ - def reserveRef(relative: Boolean): Addr = { - val addr = currentAddr - keepOffset(relative) - reserveAddr() - addr - } - - /** Write reference right adjusted into freshly reserved field. */ - def writeRef(target: Addr) = { - keepOffset(relative = false) - fillAddr(reserveAddr(), target) - } - - /** Fill previously reserved field with a reference */ - def fillRef(at: Addr, target: Addr, relative: Boolean) = { - val addr = if (relative) target.relativeTo(at) else target - fillAddr(at, addr) - } - - /** The amount by which the bytes at the given address are shifted under compression */ - def deltaAt(at: Addr): Int = { - val idx = bestFit(offsets, numOffsets, at.index - 1) - if (idx < 0) 0 else delta(idx) - } - - /** The address to which `x` is translated under compression */ - def adjusted(x: Addr): Addr = x - deltaAt(x) - - /** Compute all shift-deltas */ - private def computeDeltas() = { - delta = new Array[Int](numOffsets) - var lastDelta = 0 - var i = 0 - while (i < numOffsets) { - val off = offset(i) - val skippedOff = skipZeroes(off) - val skippedCount = skippedOff.index - off.index - assert(skippedCount < AddrWidth, s"unset field at position $off") - lastDelta += skippedCount - delta(i) = lastDelta - i += 1 - } - } - - /** The absolute or relative adjusted address at index `i` of `offsets` array*/ - private def adjustedOffset(i: Int): Addr = { - val at = offset(i) - val original = getAddr(at) - if (isRelative(i)) { - val start = skipNat(at) - val len1 = original + delta(i) - deltaAt(original + start.index) - val len2 = adjusted(original + start.index) - adjusted(start).index - assert(len1 == len2, - s"adjusting offset #$i: $at, original = $original, len1 = $len1, len2 = $len2") - len1 - } else adjusted(original) - } - - /** Adjust all offsets according to previously computed deltas */ - private def adjustOffsets(): Unit = { - for (i <- 0 until numOffsets) { - val corrected = adjustedOffset(i) - fillAddr(offset(i), corrected) - } - } - - /** Adjust deltas to also take account references that will shrink (and thereby - * generate additional zeroes that can be skipped) due to previously - * computed adjustments. - */ - private def adjustDeltas(): Int = { - val delta1 = new Array[Int](delta.length) - var lastDelta = 0 - var i = 0 - while (i < numOffsets) { - val corrected = adjustedOffset(i) - lastDelta += AddrWidth - TastyBuffer.natSize(corrected.index) - delta1(i) = lastDelta - i += 1 - } - val saved = - if (numOffsets == 0) 0 - else delta1(numOffsets - 1) - delta(numOffsets - 1) - delta = delta1 - saved - } - - /** Compress pickle buffer, shifting bytes to close all skipped zeroes. */ - private def compress(): Int = { - var lastDelta = 0 - var start = 0 - var i = 0 - var wasted = 0 - def shift(end: Int) = - Array.copy(bytes, start, bytes, start - lastDelta, end - start) - while (i < numOffsets) { - val next = offsets(i) - shift(next) - start = next + delta(i) - lastDelta - val pastZeroes = skipZeroes(Addr(next)).index - assert(pastZeroes >= start, s"something's wrong: eliminated non-zero") - wasted += (pastZeroes - start) - lastDelta = delta(i) - i += 1 - } - shift(length) - length -= lastDelta - wasted - } - - def adjustTreeAddrs(): Unit = { - val it = treeAddrs.keySet.iterator - while (it.hasNext) { - val tree = it.next - treeAddrs.get(tree) match { - case addr: Addr => treeAddrs.put(tree, adjusted(addr)) - case addrs: List[Addr] => treeAddrs.put(tree, addrs.map(adjusted)) - } - } - } - - /** Final assembly, involving the following steps: - * - compute deltas - * - adjust deltas until additional savings are < 1% of total - * - adjust offsets according to the adjusted deltas - * - shrink buffer, skipping zeroes. - */ - def compactify(): Unit = { - val origLength = length - computeDeltas() - //println(s"offsets: ${offsets.take(numOffsets).deep}") - //println(s"deltas: ${delta.take(numOffsets).deep}") - var saved = 0 - do { - saved = adjustDeltas() - pickling.println(s"adjusting deltas, saved = $saved") - } while (saved > 0 && length / saved < 100) - adjustOffsets() - adjustTreeAddrs() - val wasted = compress() - pickling.println(s"original length: $origLength, compressed to: $length, wasted: $wasted") // DEBUG, for now. - } -} diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala deleted file mode 100644 index 80270aa25..000000000 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ /dev/null @@ -1,641 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import ast.Trees._ -import ast.untpd -import TastyFormat._ -import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._ -import collection.mutable -import typer.Inliner -import NameOps._ -import StdNames.nme -import TastyBuffer._ -import TypeApplications._ - -class TreePickler(pickler: TastyPickler) { - val buf = new TreeBuffer - pickler.newSection("ASTs", buf) - import buf._ - import pickler.nameBuffer.{nameIndex, fullNameIndex} - import ast.tpd._ - - private val symRefs = new mutable.HashMap[Symbol, Addr] - private val forwardSymRefs = new mutable.HashMap[Symbol, List[Addr]] - private val pickledTypes = new java.util.IdentityHashMap[Type, Any] // Value type is really Addr, but that's not compatible with null - - private def withLength(op: => Unit) = { - val lengthAddr = reserveRef(relative = true) - op - fillRef(lengthAddr, currentAddr, relative = true) - } - - def addrOfSym(sym: Symbol): Option[Addr] = { - symRefs.get(sym) - } - - def preRegister(tree: Tree)(implicit ctx: Context): Unit = tree match { - case tree: MemberDef => - if (!symRefs.contains(tree.symbol)) symRefs(tree.symbol) = NoAddr - case _ => - } - - def registerDef(sym: Symbol): Unit = { - symRefs(sym) = currentAddr - forwardSymRefs.get(sym) match { - case Some(refs) => - refs.foreach(fillRef(_, currentAddr, relative = false)) - forwardSymRefs -= sym - case None => - } - } - - private def pickleName(name: Name): Unit = writeNat(nameIndex(name).index) - private def pickleName(name: TastyName): Unit = writeNat(nameIndex(name).index) - private def pickleNameAndSig(name: Name, sig: Signature) = { - val Signature(params, result) = sig - pickleName(TastyName.Signed(nameIndex(name), params.map(fullNameIndex), fullNameIndex(result))) - } - - private def pickleName(sym: Symbol)(implicit ctx: Context): Unit = { - def encodeSuper(name: Name): TastyName.NameRef = - if (sym is Flags.SuperAccessor) { - val SuperAccessorName(n) = name - nameIndex(TastyName.SuperAccessor(nameIndex(n))) - } - else nameIndex(name) - val nameRef = - if (sym is Flags.ExpandedName) - nameIndex( - TastyName.Expanded( - nameIndex(sym.name.expandedPrefix), - encodeSuper(sym.name.unexpandedName))) - else encodeSuper(sym.name) - writeNat(nameRef.index) - } - - private def pickleSymRef(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match { - case Some(label) => - if (label != NoAddr) writeRef(label) else pickleForwardSymRef(sym) - case None => - // See pos/t1957.scala for an example where this can happen. - // I believe it's a bug in typer: the type of an implicit argument refers - // to a closure parameter outside the closure itself. TODO: track this down, so that we - // can eliminate this case. - ctx.log(i"pickling reference to as yet undefined $sym in ${sym.owner}", sym.pos) - pickleForwardSymRef(sym) - } - - private def pickleForwardSymRef(sym: Symbol)(implicit ctx: Context) = { - val ref = reserveRef(relative = false) - assert(!sym.is(Flags.Package), sym) - forwardSymRefs(sym) = ref :: forwardSymRefs.getOrElse(sym, Nil) - } - - private def isLocallyDefined(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match { - case Some(label) => assert(sym.exists); label != NoAddr - case None => false - } - - def pickleConstant(c: Constant)(implicit ctx: Context): Unit = c.tag match { - case UnitTag => - writeByte(UNITconst) - case BooleanTag => - writeByte(if (c.booleanValue) TRUEconst else FALSEconst) - case ByteTag => - writeByte(BYTEconst) - writeInt(c.byteValue) - case ShortTag => - writeByte(SHORTconst) - writeInt(c.shortValue) - case CharTag => - writeByte(CHARconst) - writeNat(c.charValue) - case IntTag => - writeByte(INTconst) - writeInt(c.intValue) - case LongTag => - writeByte(LONGconst) - writeLongInt(c.longValue) - case FloatTag => - writeByte(FLOATconst) - writeInt(java.lang.Float.floatToRawIntBits(c.floatValue)) - case DoubleTag => - writeByte(DOUBLEconst) - writeLongInt(java.lang.Double.doubleToRawLongBits(c.doubleValue)) - case StringTag => - writeByte(STRINGconst) - writeNat(nameIndex(c.stringValue).index) - case NullTag => - writeByte(NULLconst) - case ClazzTag => - writeByte(CLASSconst) - pickleType(c.typeValue) - case EnumTag => - writeByte(ENUMconst) - pickleType(c.symbolValue.termRef) - } - - def pickleType(tpe0: Type, richTypes: Boolean = false)(implicit ctx: Context): Unit = try { - val tpe = tpe0.stripTypeVar - val prev = pickledTypes.get(tpe) - if (prev == null) { - pickledTypes.put(tpe, currentAddr) - pickleNewType(tpe, richTypes) - } - else { - writeByte(SHARED) - writeRef(prev.asInstanceOf[Addr]) - } - } catch { - case ex: AssertionError => - println(i"error when pickling type $tpe0") - throw ex - } - - private def pickleNewType(tpe: Type, richTypes: Boolean)(implicit ctx: Context): Unit = try { tpe match { - case AppliedType(tycon, args) => - writeByte(APPLIEDtype) - withLength { pickleType(tycon); args.foreach(pickleType(_)) } - case ConstantType(value) => - pickleConstant(value) - case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.is(Flags.AliasPreferred) => - pickleType(tpe.superType) - case tpe: WithFixedSym => - val sym = tpe.symbol - def pickleRef() = - if (tpe.prefix == NoPrefix) { - writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect) - pickleSymRef(sym) - } - else { - assert(tpe.symbol.isClass) - assert(tpe.symbol.is(Flags.Scala2x), tpe.symbol.showLocated) - writeByte(TYPEREF) // should be changed to a new entry that keeps track of prefix, symbol & owner - pickleName(tpe.name) - pickleType(tpe.prefix) - } - if (sym.is(Flags.Package)) { - writeByte(if (tpe.isType) TYPEREFpkg else TERMREFpkg) - pickleName(qualifiedName(sym)) - } - else if (sym is Flags.BindDefinedType) { - registerDef(sym) - writeByte(BIND) - withLength { - pickleName(sym.name) - pickleType(sym.info) - pickleRef() - } - } - else pickleRef() - case tpe: TermRefWithSignature => - if (tpe.symbol.is(Flags.Package)) picklePackageRef(tpe.symbol) - else { - writeByte(TERMREF) - pickleNameAndSig(tpe.name, tpe.signature); pickleType(tpe.prefix) - } - case tpe: NamedType => - if (isLocallyDefined(tpe.symbol)) { - writeByte(if (tpe.isType) TYPEREFsymbol else TERMREFsymbol) - pickleSymRef(tpe.symbol); pickleType(tpe.prefix) - } else { - writeByte(if (tpe.isType) TYPEREF else TERMREF) - pickleName(tpe.name); pickleType(tpe.prefix) - } - case tpe: ThisType => - if (tpe.cls.is(Flags.Package) && !tpe.cls.isEffectiveRoot) - picklePackageRef(tpe.cls) - else { - writeByte(THIS) - pickleType(tpe.tref) - } - case tpe: SuperType => - writeByte(SUPERtype) - withLength { pickleType(tpe.thistpe); pickleType(tpe.supertpe)} - case tpe: RecThis => - writeByte(RECthis) - val binderAddr = pickledTypes.get(tpe.binder) - assert(binderAddr != null, tpe.binder) - writeRef(binderAddr.asInstanceOf[Addr]) - case tpe: SkolemType => - pickleType(tpe.info) - case tpe: RefinedType => - writeByte(REFINEDtype) - withLength { - pickleName(tpe.refinedName) - pickleType(tpe.parent) - pickleType(tpe.refinedInfo, richTypes = true) - } - case tpe: RecType => - writeByte(RECtype) - pickleType(tpe.parent) - case tpe: TypeAlias => - writeByte(TYPEALIAS) - withLength { - pickleType(tpe.alias, richTypes) - tpe.variance match { - case 1 => writeByte(COVARIANT) - case -1 => writeByte(CONTRAVARIANT) - case 0 => - } - } - case tpe: TypeBounds => - writeByte(TYPEBOUNDS) - withLength { pickleType(tpe.lo, richTypes); pickleType(tpe.hi, richTypes) } - case tpe: AnnotatedType => - writeByte(ANNOTATEDtype) - withLength { pickleType(tpe.tpe, richTypes); pickleTree(tpe.annot.tree) } - case tpe: AndOrType => - writeByte(if (tpe.isAnd) ANDtype else ORtype) - withLength { pickleType(tpe.tp1, richTypes); pickleType(tpe.tp2, richTypes) } - case tpe: ExprType => - writeByte(BYNAMEtype) - pickleType(tpe.underlying) - case tpe: PolyType => - writeByte(POLYtype) - val paramNames = tpe.typeParams.map(tparam => - varianceToPrefix(tparam.paramVariance) +: tparam.paramName) - pickleMethodic(tpe.resultType, paramNames, tpe.paramBounds) - case tpe: MethodType if richTypes => - writeByte(METHODtype) - pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramTypes) - case tpe: PolyParam => - if (!pickleParamType(tpe)) - // TODO figure out why this case arises in e.g. pickling AbstractFileReader. - ctx.typerState.constraint.entry(tpe) match { - case TypeBounds(lo, hi) if lo eq hi => pickleNewType(lo, richTypes) - case _ => assert(false, s"orphan poly parameter: $tpe") - } - case tpe: MethodParam => - assert(pickleParamType(tpe), s"orphan method parameter: $tpe") - case tpe: LazyRef => - pickleType(tpe.ref) - }} catch { - case ex: AssertionError => - println(i"error while pickling type $tpe") - throw ex - } - - def picklePackageRef(pkg: Symbol)(implicit ctx: Context): Unit = { - writeByte(TERMREFpkg) - pickleName(qualifiedName(pkg)) - } - - def pickleMethodic(result: Type, names: List[Name], types: List[Type])(implicit ctx: Context) = - withLength { - pickleType(result, richTypes = true) - (names, types).zipped.foreach { (name, tpe) => - pickleName(name); pickleType(tpe) - } - } - - def pickleParamType(tpe: ParamType)(implicit ctx: Context): Boolean = { - val binder = pickledTypes.get(tpe.binder) - val pickled = binder != null - if (pickled) { - writeByte(PARAMtype) - withLength { writeRef(binder.asInstanceOf[Addr]); writeNat(tpe.paramNum) } - } - pickled - } - - def pickleTpt(tpt: Tree)(implicit ctx: Context): Unit = - pickleTree(tpt) - - def pickleTreeUnlessEmpty(tree: Tree)(implicit ctx: Context): Unit = - if (!tree.isEmpty) pickleTree(tree) - - def pickleDef(tag: Int, sym: Symbol, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ())(implicit ctx: Context) = { - assert(symRefs(sym) == NoAddr, sym) - registerDef(sym) - writeByte(tag) - withLength { - pickleName(sym) - pickleParams - tpt match { - case templ: Template => pickleTree(tpt) - case _ if tpt.isType => pickleTpt(tpt) - } - pickleTreeUnlessEmpty(rhs) - pickleModifiers(sym) - } - } - - def pickleParam(tree: Tree)(implicit ctx: Context): Unit = { - registerTreeAddr(tree) - tree match { - case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt) - case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs) - case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, tree.rhs) - } - } - - def pickleParams(trees: List[Tree])(implicit ctx: Context): Unit = { - trees.foreach(preRegister) - trees.foreach(pickleParam) - } - - def pickleStats(stats: List[Tree])(implicit ctx: Context) = { - stats.foreach(preRegister) - stats.foreach(stat => if (!stat.isEmpty) pickleTree(stat)) - } - - def pickleTree(tree: Tree)(implicit ctx: Context): Unit = { - val addr = registerTreeAddr(tree) - if (addr != currentAddr) { - writeByte(SHARED) - writeRef(addr) - } - else - try tree match { - case Ident(name) => - tree.tpe match { - case tp: TermRef if name != nme.WILDCARD => - // wildcards are pattern bound, need to be preserved as ids. - pickleType(tp) - case _ => - writeByte(if (tree.isType) IDENTtpt else IDENT) - pickleName(name) - pickleType(tree.tpe) - } - case This(qual) => - if (qual.isEmpty) pickleType(tree.tpe) - else { - writeByte(QUALTHIS) - val ThisType(tref) = tree.tpe - pickleTree(qual.withType(tref)) - } - case Select(qual, name) => - writeByte(if (name.isTypeName) SELECTtpt else SELECT) - val realName = tree.tpe match { - case tp: NamedType if tp.name.isShadowedName => tp.name - case _ => name - } - val sig = tree.tpe.signature - if (sig == Signature.NotAMethod) pickleName(realName) - else pickleNameAndSig(realName, sig) - pickleTree(qual) - case Apply(fun, args) => - writeByte(APPLY) - withLength { - pickleTree(fun) - args.foreach(pickleTree) - } - case TypeApply(fun, args) => - writeByte(TYPEAPPLY) - withLength { - pickleTree(fun) - args.foreach(pickleTpt) - } - case Literal(const1) => - pickleConstant { - tree.tpe match { - case ConstantType(const2) => const2 - case _ => const1 - } - } - case Super(qual, mix) => - writeByte(SUPER) - withLength { - pickleTree(qual); - if (!mix.isEmpty) { - val SuperType(_, mixinType: TypeRef) = tree.tpe - pickleTree(mix.withType(mixinType)) - } - } - case New(tpt) => - writeByte(NEW) - pickleTpt(tpt) - case Typed(expr, tpt) => - writeByte(TYPED) - withLength { pickleTree(expr); pickleTpt(tpt) } - case NamedArg(name, arg) => - writeByte(NAMEDARG) - withLength { pickleName(name); pickleTree(arg) } - case Assign(lhs, rhs) => - writeByte(ASSIGN) - withLength { pickleTree(lhs); pickleTree(rhs) } - case Block(stats, expr) => - writeByte(BLOCK) - stats.foreach(preRegister) - withLength { pickleTree(expr); stats.foreach(pickleTree) } - case If(cond, thenp, elsep) => - writeByte(IF) - withLength { pickleTree(cond); pickleTree(thenp); pickleTree(elsep) } - case Closure(env, meth, tpt) => - writeByte(LAMBDA) - assert(env.isEmpty) - withLength { - pickleTree(meth) - if (tpt.tpe.exists) pickleTpt(tpt) - } - case Match(selector, cases) => - writeByte(MATCH) - withLength { pickleTree(selector); cases.foreach(pickleTree) } - case CaseDef(pat, guard, rhs) => - writeByte(CASEDEF) - withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) } - case Return(expr, from) => - writeByte(RETURN) - withLength { pickleSymRef(from.symbol); pickleTreeUnlessEmpty(expr) } - case Try(block, cases, finalizer) => - writeByte(TRY) - withLength { pickleTree(block); cases.foreach(pickleTree); pickleTreeUnlessEmpty(finalizer) } - case SeqLiteral(elems, elemtpt) => - writeByte(REPEATED) - withLength { pickleTree(elemtpt); elems.foreach(pickleTree) } - case Inlined(call, bindings, expansion) => - writeByte(INLINED) - bindings.foreach(preRegister) - withLength { pickleTree(call); pickleTree(expansion); bindings.foreach(pickleTree) } - case Bind(name, body) => - registerDef(tree.symbol) - writeByte(BIND) - withLength { pickleName(name); pickleType(tree.symbol.info); pickleTree(body) } - case Alternative(alts) => - writeByte(ALTERNATIVE) - withLength { alts.foreach(pickleTree) } - case UnApply(fun, implicits, patterns) => - writeByte(UNAPPLY) - withLength { - pickleTree(fun) - for (implicitArg <- implicits) { - writeByte(IMPLICITarg) - pickleTree(implicitArg) - } - pickleType(tree.tpe) - patterns.foreach(pickleTree) - } - case tree: ValDef => - pickleDef(VALDEF, tree.symbol, tree.tpt, tree.rhs) - case tree: DefDef => - def pickleAllParams = { - pickleParams(tree.tparams) - for (vparams <- tree.vparamss) { - writeByte(PARAMS) - withLength { pickleParams(vparams) } - } - } - pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) - case tree: TypeDef => - pickleDef(TYPEDEF, tree.symbol, tree.rhs) - case tree: Template => - registerDef(tree.symbol) - writeByte(TEMPLATE) - val (params, rest) = tree.body partition { - case stat: TypeDef => stat.symbol is Flags.Param - case stat: ValOrDefDef => - stat.symbol.is(Flags.ParamAccessor) && !stat.symbol.isSetter - case _ => false - } - withLength { - pickleParams(params) - tree.parents.foreach(pickleTree) - val cinfo @ ClassInfo(_, _, _, _, selfInfo) = tree.symbol.owner.info - if ((selfInfo ne NoType) || !tree.self.isEmpty) { - writeByte(SELFDEF) - pickleName(tree.self.name) - - if (!tree.self.tpt.isEmpty) pickleTree(tree.self.tpt) - else { - if (!tree.self.isEmpty) registerTreeAddr(tree.self) - pickleType { - cinfo.selfInfo match { - case sym: Symbol => sym.info - case tp: Type => tp - } - } - } - } - pickleStats(tree.constr :: rest) - } - case Import(expr, selectors) => - writeByte(IMPORT) - withLength { - pickleTree(expr) - selectors foreach { - case Thicket((from @ Ident(_)) :: (to @ Ident(_)) :: Nil) => - pickleSelector(IMPORTED, from) - pickleSelector(RENAMED, to) - case id @ Ident(_) => - pickleSelector(IMPORTED, id) - } - } - case PackageDef(pid, stats) => - writeByte(PACKAGE) - withLength { pickleType(pid.tpe); pickleStats(stats) } - case tree: TypeTree => - pickleType(tree.tpe) - case SingletonTypeTree(ref) => - writeByte(SINGLETONtpt) - pickleTree(ref) - case RefinedTypeTree(parent, refinements) => - if (refinements.isEmpty) pickleTree(parent) - else { - val refineCls = refinements.head.symbol.owner.asClass - pickledTypes.put(refineCls.typeRef, currentAddr) - writeByte(REFINEDtpt) - refinements.foreach(preRegister) - withLength { pickleTree(parent); refinements.foreach(pickleTree) } - } - case AppliedTypeTree(tycon, args) => - writeByte(APPLIEDtpt) - withLength { pickleTree(tycon); args.foreach(pickleTree) } - case AndTypeTree(tp1, tp2) => - writeByte(ANDtpt) - withLength { pickleTree(tp1); pickleTree(tp2) } - case OrTypeTree(tp1, tp2) => - writeByte(ORtpt) - withLength { pickleTree(tp1); pickleTree(tp2) } - case ByNameTypeTree(tp) => - writeByte(BYNAMEtpt) - pickleTree(tp) - case Annotated(tree, annot) => - writeByte(ANNOTATEDtpt) - withLength { pickleTree(tree); pickleTree(annot.tree) } - case PolyTypeTree(tparams, body) => - writeByte(POLYtpt) - withLength { pickleParams(tparams); pickleTree(body) } - case TypeBoundsTree(lo, hi) => - writeByte(TYPEBOUNDStpt) - withLength { pickleTree(lo); pickleTree(hi) } - } - catch { - case ex: AssertionError => - println(i"error when pickling tree $tree") - throw ex - } - } - - def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = { - registerTreeAddr(id) - writeByte(tag) - pickleName(id.name) - } - - def qualifiedName(sym: Symbol)(implicit ctx: Context): TastyName = - if (sym.isRoot || sym.owner.isRoot) TastyName.Simple(sym.name.toTermName) - else TastyName.Qualified(nameIndex(qualifiedName(sym.owner)), nameIndex(sym.name)) - - def pickleModifiers(sym: Symbol)(implicit ctx: Context): Unit = { - import Flags._ - val flags = sym.flags - val privateWithin = sym.privateWithin - if (privateWithin.exists) { - writeByte(if (flags is Protected) PROTECTEDqualified else PRIVATEqualified) - pickleType(privateWithin.typeRef) - } - if (flags is Private) writeByte(PRIVATE) - if (flags is Protected) if (!privateWithin.exists) writeByte(PROTECTED) - if ((flags is Final) && !(sym is Module)) writeByte(FINAL) - if (flags is Case) writeByte(CASE) - if (flags is Override) writeByte(OVERRIDE) - if (flags is Inline) writeByte(INLINE) - if (flags is JavaStatic) writeByte(STATIC) - if (flags is Module) writeByte(OBJECT) - if (flags is Local) writeByte(LOCAL) - if (flags is Synthetic) writeByte(SYNTHETIC) - if (flags is Artifact) writeByte(ARTIFACT) - if (flags is Scala2x) writeByte(SCALA2X) - if (flags is InSuperCall) writeByte(INSUPERCALL) - if (sym.isTerm) { - if (flags is Implicit) writeByte(IMPLICIT) - if ((flags is Lazy) && !(sym is Module)) writeByte(LAZY) - if (flags is AbsOverride) { writeByte(ABSTRACT); writeByte(OVERRIDE) } - if (flags is Mutable) writeByte(MUTABLE) - if (flags is Accessor) writeByte(FIELDaccessor) - if (flags is CaseAccessor) writeByte(CASEaccessor) - if (flags is DefaultParameterized) writeByte(DEFAULTparameterized) - if (flags is Stable) writeByte(STABLE) - } else { - if (flags is Sealed) writeByte(SEALED) - if (flags is Abstract) writeByte(ABSTRACT) - if (flags is Trait) writeByte(TRAIT) - if (flags is Covariant) writeByte(COVARIANT) - if (flags is Contravariant) writeByte(CONTRAVARIANT) - } - sym.annotations.foreach(pickleAnnotation) - } - - def pickleAnnotation(ann: Annotation)(implicit ctx: Context) = - if (ann.symbol != defn.BodyAnnot) { // inline bodies are reconstituted automatically when unpickling - writeByte(ANNOTATION) - withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) } - } - - def pickle(trees: List[Tree])(implicit ctx: Context) = { - trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree)) - assert(forwardSymRefs.isEmpty, i"unresolved symbols: ${forwardSymRefs.keySet.toList}%, % when pickling ${ctx.source}") - } - - def compactify() = { - buf.compactify() - - def updateMapWithDeltas[T](mp: collection.mutable.Map[T, Addr]) = - for (key <- mp.keysIterator.toBuffer[T]) mp(key) = adjusted(mp(key)) - - updateMapWithDeltas(symRefs) - } -} diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala deleted file mode 100644 index eba9ab533..000000000 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ /dev/null @@ -1,1161 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ -import StdNames._, Denotations._, Flags._, Constants._, Annotations._ -import util.Positions._ -import ast.{tpd, Trees, untpd} -import Trees._ -import Decorators._ -import TastyUnpickler._, TastyBuffer._ -import scala.annotation.{tailrec, switch} -import scala.collection.mutable.ListBuffer -import scala.collection.{ mutable, immutable } -import config.Printers.pickling - -/** Unpickler for typed trees - * @param reader the reader from which to unpickle - * @param tastyName the nametable - * @param posUNpicklerOpt the unpickler for positions, if it exists - */ -class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpicklerOpt: Option[PositionUnpickler]) { - import TastyFormat._ - import TastyName._ - import TreeUnpickler._ - import tpd._ - - /** A map from addresses of definition entries to the symbols they define */ - private val symAtAddr = new mutable.HashMap[Addr, Symbol] - - /** A temporary map from addresses of definition entries to the trees they define. - * Used to remember trees of symbols that are created by a completion. Emptied - * once the tree is inlined into a larger tree. - */ - private val treeAtAddr = new mutable.HashMap[Addr, Tree] - - /** A map from addresses of type entries to the types they define. - * Currently only populated for types that might be recursively referenced - * from within themselves (i.e. RefinedTypes, PolyTypes, MethodTypes). - */ - private val typeAtAddr = new mutable.HashMap[Addr, Type] - - /** The root symbol denotation which are defined by the Tasty file associated with this - * TreeUnpickler. Set by `enterTopLevel`. - */ - private var roots: Set[SymDenotation] = null - - /** The root symbols that are defined in this Tasty file. This - * is a subset of `roots.map(_.symbol)`. - */ - private var seenRoots: Set[Symbol] = Set() - - /** The root owner tree. See `OwnerTree` class definition. Set by `enterTopLevel`. */ - private var ownerTree: OwnerTree = _ - - private def registerSym(addr: Addr, sym: Symbol) = - symAtAddr(addr) = sym - - /** Enter all toplevel classes and objects into their scopes - * @param roots a set of SymDenotations that should be overwritten by unpickling - */ - def enterTopLevel(roots: Set[SymDenotation])(implicit ctx: Context): Unit = { - this.roots = roots - var rdr = new TreeReader(reader).fork - ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr) - rdr.indexStats(reader.endAddr) - } - - /** The unpickled trees */ - def unpickle()(implicit ctx: Context): List[Tree] = { - assert(roots != null, "unpickle without previous enterTopLevel") - new TreeReader(reader).readTopLevel()(ctx.addMode(Mode.AllowDependentFunctions)) - } - - def toTermName(tname: TastyName): TermName = tname match { - case Simple(name) => name - case Qualified(qual, name) => toTermName(qual) ++ "." ++ toTermName(name) - case Signed(original, params, result) => toTermName(original) - case Shadowed(original) => toTermName(original).shadowedName - case Expanded(prefix, original) => toTermName(original).expandedName(toTermName(prefix)) - case ModuleClass(original) => toTermName(original).moduleClassName.toTermName - case SuperAccessor(accessed) => toTermName(accessed).superName - case DefaultGetter(meth, num) => ??? - } - - def toTermName(ref: NameRef): TermName = toTermName(tastyName(ref)) - def toTypeName(ref: NameRef): TypeName = toTermName(ref).toTypeName - - class Completer(owner: Symbol, reader: TastyReader) extends LazyType { - import reader._ - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - treeAtAddr(currentAddr) = - new TreeReader(reader).readIndexedDef()( - ctx.withPhaseNoLater(ctx.picklerPhase).withOwner(owner)) - } - } - - class TreeReader(val reader: TastyReader) { - import reader._ - - def forkAt(start: Addr) = new TreeReader(subReader(start, endAddr)) - def fork = forkAt(currentAddr) - - def skipTree(tag: Int): Unit = - if (tag >= firstLengthTreeTag) goto(readEnd()) - else if (tag >= firstNatASTTreeTag) { readNat(); skipTree() } - else if (tag >= firstASTTreeTag) skipTree() - else if (tag >= firstNatTreeTag) readNat() - def skipTree(): Unit = skipTree(readByte()) - - def skipParams(): Unit = - while (nextByte == PARAMS || nextByte == TYPEPARAM) skipTree() - - /** Record all directly nested definitions and templates in current tree - * as `OwnerTree`s in `buf` - */ - def scanTree(buf: ListBuffer[OwnerTree], mode: MemberDefMode = AllDefs): Unit = { - val start = currentAddr - val tag = readByte() - tag match { - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | TEMPLATE => - val end = readEnd() - for (i <- 0 until numRefs(tag)) readNat() - if (tag == TEMPLATE) scanTrees(buf, end, MemberDefsOnly) - if (mode != NoMemberDefs) buf += new OwnerTree(start, tag, fork, end) - goto(end) - case tag => - if (mode == MemberDefsOnly) skipTree(tag) - else if (tag >= firstLengthTreeTag) { - val end = readEnd() - var nrefs = numRefs(tag) - if (nrefs < 0) { - for (i <- nrefs until 0) scanTree(buf) - goto(end) - } - else { - for (i <- 0 until nrefs) readNat() - scanTrees(buf, end) - } - } - else if (tag >= firstNatASTTreeTag) { readNat(); scanTree(buf) } - else if (tag >= firstASTTreeTag) scanTree(buf) - else if (tag >= firstNatTreeTag) readNat() - } - } - - /** Record all directly nested definitions and templates between current address and `end` - * as `OwnerTree`s in `buf` - */ - def scanTrees(buf: ListBuffer[OwnerTree], end: Addr, mode: MemberDefMode = AllDefs): Unit = { - while (currentAddr.index < end.index) scanTree(buf, mode) - assert(currentAddr.index == end.index) - } - - /** The next tag, following through SHARED tags */ - def nextUnsharedTag: Int = { - val tag = nextByte - if (tag == SHARED) { - val lookAhead = fork - lookAhead.reader.readByte() - forkAt(lookAhead.reader.readAddr()).nextUnsharedTag - } - else tag - } - - def readName(): TermName = toTermName(readNameRef()) - - def readNameSplitSig()(implicit ctx: Context): Any /* TermName | (TermName, Signature) */ = - tastyName(readNameRef()) match { - case Signed(original, params, result) => - var sig = Signature(params map toTypeName, toTypeName(result)) - if (sig == Signature.NotAMethod) sig = Signature.NotAMethod - (toTermName(original), sig) - case name => - toTermName(name) - } - -// ------ Reading types ----------------------------------------------------- - - /** Read names in an interleaved sequence of (parameter) names and types/bounds */ - def readParamNames(end: Addr): List[Name] = - until(end) { - val name = readName() - skipTree() - name - } - - /** Read types or bounds in an interleaved sequence of (parameter) names and types/bounds */ - def readParamTypes[T <: Type](end: Addr)(implicit ctx: Context): List[T] = - until(end) { readNat(); readType().asInstanceOf[T] } - - /** Read referece to definition and return symbol created at that definition */ - def readSymRef()(implicit ctx: Context): Symbol = symbolAt(readAddr()) - - /** The symbol at given address; createa new one if none exists yet */ - def symbolAt(addr: Addr)(implicit ctx: Context): Symbol = symAtAddr.get(addr) match { - case Some(sym) => - sym - case None => - val sym = forkAt(addr).createSymbol()(ctx.withOwner(ownerTree.findOwner(addr))) - ctx.log(i"forward reference to $sym") - sym - } - - /** The symbol defined by current definition */ - def symbolAtCurrent()(implicit ctx: Context): Symbol = symAtAddr.get(currentAddr) match { - case Some(sym) => - assert(ctx.owner == sym.owner, i"owner discrepancy for $sym, expected: ${ctx.owner}, found: ${sym.owner}") - sym - case None => - createSymbol() - } - - /** Read a type */ - def readType()(implicit ctx: Context): Type = { - val start = currentAddr - val tag = readByte() - pickling.println(s"reading type ${astTagToString(tag)} at $start") - - def registeringType[T](tp: Type, op: => T): T = { - typeAtAddr(start) = tp - op - } - - def readLengthType(): Type = { - val end = readEnd() - - def readNamesSkipParams: (List[Name], TreeReader) = { - val nameReader = fork - nameReader.skipTree() // skip result - val paramReader = nameReader.fork - (nameReader.readParamNames(end), paramReader) - } - - val result = - (tag: @switch) match { - case SUPERtype => - SuperType(readType(), readType()) - case REFINEDtype => - var name: Name = readName() - val parent = readType() - val ttag = nextUnsharedTag - if (ttag == TYPEBOUNDS || ttag == TYPEALIAS) name = name.toTypeName - RefinedType(parent, name, readType()) - // Note that the lambda "rt => ..." is not equivalent to a wildcard closure! - // Eta expansion of the latter puts readType() out of the expression. - case APPLIEDtype => - readType().appliedTo(until(end)(readType())) - case TYPEBOUNDS => - TypeBounds(readType(), readType()) - case TYPEALIAS => - val alias = readType() - val variance = - if (nextByte == COVARIANT) { readByte(); 1 } - else if (nextByte == CONTRAVARIANT) { readByte(); -1 } - else 0 - TypeAlias(alias, variance) - case ANNOTATEDtype => - AnnotatedType(readType(), Annotation(readTerm())) - case ANDtype => - AndType(readType(), readType()) - case ORtype => - OrType(readType(), readType()) - case BIND => - val sym = ctx.newSymbol(ctx.owner, readName().toTypeName, BindDefinedType, readType()) - registerSym(start, sym) - TypeRef.withFixedSym(NoPrefix, sym.name, sym) - case POLYtype => - val (rawNames, paramReader) = readNamesSkipParams - val (variances, paramNames) = rawNames - .map(name => (prefixToVariance(name.head), name.tail.toTypeName)).unzip - val result = PolyType(paramNames, variances)( - pt => registeringType(pt, paramReader.readParamTypes[TypeBounds](end)), - pt => readType()) - goto(end) - result - case METHODtype => - val (names, paramReader) = readNamesSkipParams - val result = MethodType(names.map(_.toTermName), paramReader.readParamTypes[Type](end))( - mt => registeringType(mt, readType())) - goto(end) - result - case PARAMtype => - readTypeRef() match { - case binder: PolyType => PolyParam(binder, readNat()) - case binder: MethodType => MethodParam(binder, readNat()) - } - case CLASSconst => - ConstantType(Constant(readType())) - case ENUMconst => - ConstantType(Constant(readTermRef().termSymbol)) - } - assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") - result - } - - def readSimpleType(): Type = (tag: @switch) match { - case TYPEREFdirect | TERMREFdirect => - NamedType.withFixedSym(NoPrefix, readSymRef()) - case TYPEREFsymbol | TERMREFsymbol => - readSymNameRef() - case TYPEREFpkg => - readPackageRef().moduleClass.typeRef - case TERMREFpkg => - readPackageRef().termRef - case TYPEREF => - val name = readName().toTypeName - TypeRef(readType(), name) - case TERMREF => - readNameSplitSig() match { - case name: TermName => TermRef.all(readType(), name) - case (name: TermName, sig: Signature) => TermRef.withSig(readType(), name, sig) - } - case THIS => - ThisType.raw(readType().asInstanceOf[TypeRef]) - case RECtype => - RecType(rt => registeringType(rt, readType())) - case RECthis => - RecThis(readTypeRef().asInstanceOf[RecType]) - case SHARED => - val ref = readAddr() - typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) - case UNITconst => - ConstantType(Constant(())) - case TRUEconst => - ConstantType(Constant(true)) - case FALSEconst => - ConstantType(Constant(false)) - case BYTEconst => - ConstantType(Constant(readInt().toByte)) - case SHORTconst => - ConstantType(Constant(readInt().toShort)) - case CHARconst => - ConstantType(Constant(readNat().toChar)) - case INTconst => - ConstantType(Constant(readInt())) - case LONGconst => - ConstantType(Constant(readLongInt())) - case FLOATconst => - ConstantType(Constant(java.lang.Float.intBitsToFloat(readInt()))) - case DOUBLEconst => - ConstantType(Constant(java.lang.Double.longBitsToDouble(readLongInt()))) - case STRINGconst => - ConstantType(Constant(readName().toString)) - case NULLconst => - ConstantType(Constant(null)) - case CLASSconst => - ConstantType(Constant(readType())) - case BYNAMEtype => - ExprType(readType()) - } - - if (tag < firstLengthTreeTag) readSimpleType() else readLengthType() - } - - private def readSymNameRef()(implicit ctx: Context): Type = { - val sym = readSymRef() - val prefix = readType() - val res = NamedType.withSymAndName(prefix, sym, sym.name) - prefix match { - case prefix: ThisType if prefix.cls eq sym.owner => res.withDenot(sym.denot) - // without this precaution we get an infinite cycle when unpickling pos/extmethods.scala - // the problem arises when a self type of a trait is a type parameter of the same trait. - case _ => res - } - } - - private def readPackageRef()(implicit ctx: Context): TermSymbol = { - val name = readName() - if (name == nme.ROOT || name == nme.ROOTPKG) defn.RootPackage - else if (name == nme.EMPTY_PACKAGE) defn.EmptyPackageVal - else ctx.requiredPackage(name) - } - - def readTypeRef(): Type = - typeAtAddr(readAddr()) - - def readTermRef()(implicit ctx: Context): TermRef = - readType().asInstanceOf[TermRef] - -// ------ Reading definitions ----------------------------------------------------- - - private def noRhs(end: Addr): Boolean = - currentAddr == end || isModifierTag(nextByte) - - private def localContext(owner: Symbol)(implicit ctx: Context) = { - val lctx = ctx.fresh.setOwner(owner) - if (owner.isClass) lctx.setScope(owner.unforcedDecls) else lctx.setNewScope - } - - private def normalizeFlags(tag: Int, givenFlags: FlagSet, name: Name, isAbsType: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = { - val lacksDefinition = - rhsIsEmpty && - name.isTermName && !name.isConstructorName && !givenFlags.is(ParamOrAccessor) || - isAbsType - var flags = givenFlags - if (lacksDefinition && tag != PARAM) flags |= Deferred - if (tag == DEFDEF) flags |= Method - if (givenFlags is Module) - flags = flags | (if (tag == VALDEF) ModuleCreationFlags else ModuleClassCreationFlags) - if (ctx.owner.isClass) { - if (tag == TYPEPARAM) flags |= Param - else if (tag == PARAM) flags |= ParamAccessor - } - else if (isParamTag(tag)) flags |= Param - flags - } - - def isAbstractType(ttag: Int)(implicit ctx: Context): Boolean = nextUnsharedTag match { - case POLYtpt => - val rdr = fork - rdr.reader.readByte() // tag - rdr.reader.readNat() // length - rdr.skipParams() // tparams - rdr.isAbstractType(rdr.nextUnsharedTag) - case TYPEBOUNDS | TYPEBOUNDStpt => true - case _ => false - } - - /** Create symbol of definition node and enter in symAtAddr map - * @return the created symbol - */ - def createSymbol()(implicit ctx: Context): Symbol = nextByte match { - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM => - createMemberSymbol() - case TEMPLATE => - val localDummy = ctx.newLocalDummy(ctx.owner) - registerSym(currentAddr, localDummy) - localDummy - case tag => - throw new Error(s"illegal createSymbol at $currentAddr, tag = $tag") - } - - /** Create symbol of member definition or parameter node and enter in symAtAddr map - * @return the created symbol - */ - def createMemberSymbol()(implicit ctx: Context): Symbol = { - val start = currentAddr - val tag = readByte() - val end = readEnd() - val rawName = tastyName(readNameRef()) - var name: Name = toTermName(rawName) - if (tag == TYPEDEF || tag == TYPEPARAM) name = name.toTypeName - skipParams() - val ttag = nextUnsharedTag - val isAbsType = isAbstractType(ttag) - val isClass = ttag == TEMPLATE - val templateStart = currentAddr - skipTree() // tpt - val rhsStart = currentAddr - val rhsIsEmpty = noRhs(end) - if (!rhsIsEmpty) skipTree() - val (givenFlags, annots, privateWithin) = readModifiers(end) - def nameFlags(tname: TastyName): FlagSet = tname match { - case TastyName.Expanded(_, original) => ExpandedName | nameFlags(tastyName(original)) - case TastyName.SuperAccessor(_) => Flags.SuperAccessor - case _ => EmptyFlags - } - pickling.println(i"creating symbol $name at $start with flags $givenFlags") - val flags = normalizeFlags(tag, givenFlags | nameFlags(rawName), name, isAbsType, rhsIsEmpty) - def adjustIfModule(completer: LazyType) = - if (flags is Module) ctx.adjustModuleCompleter(completer, name) else completer - val sym = - roots.find(root => (root.owner eq ctx.owner) && root.name == name) match { - case Some(rootd) => - pickling.println(i"overwriting ${rootd.symbol} # ${rootd.hashCode}") - rootd.info = adjustIfModule( - new Completer(ctx.owner, subReader(start, end)) with SymbolLoaders.SecondCompleter) - rootd.flags = flags &~ Touched // allow one more completion - rootd.privateWithin = privateWithin - seenRoots += rootd.symbol - rootd.symbol - case _ => - val completer = adjustIfModule(new Completer(ctx.owner, subReader(start, end))) - if (isClass) - ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer, privateWithin, coord = start.index) - else - ctx.newSymbol(ctx.owner, name, flags, completer, privateWithin, coord = start.index) - } // TODO set position somehow (but take care not to upset Symbol#isDefinedInCurrentRun) - sym.annotations = annots - ctx.enter(sym) - registerSym(start, sym) - if (isClass) { - sym.completer.withDecls(newScope) - forkAt(templateStart).indexTemplateParams()(localContext(sym)) - } - else if (sym.isInlineMethod) - sym.addAnnotation(LazyBodyAnnotation { ctx0 => - implicit val ctx: Context = localContext(sym)(ctx0).addMode(Mode.ReadPositions) - // avoids space leaks by not capturing the current context - forkAt(rhsStart).readTerm() - }) - goto(start) - sym - } - - /** Read modifier list into triplet of flags, annotations and a privateWithin - * boundary symbol. - */ - def readModifiers(end: Addr)(implicit ctx: Context): (FlagSet, List[Annotation], Symbol) = { - var flags: FlagSet = EmptyFlags - var annots = new mutable.ListBuffer[Annotation] - var privateWithin: Symbol = NoSymbol - while (currentAddr.index != end.index) { - def addFlag(flag: FlagSet) = { - flags |= flag - readByte() - } - nextByte match { - case PRIVATE => addFlag(Private) - case INTERNAL => ??? // addFlag(Internal) - case PROTECTED => addFlag(Protected) - case ABSTRACT => - readByte() - nextByte match { - case OVERRIDE => addFlag(AbsOverride) - case _ => flags |= Abstract - } - case FINAL => addFlag(Final) - case SEALED => addFlag(Sealed) - case CASE => addFlag(Case) - case IMPLICIT => addFlag(Implicit) - case LAZY => addFlag(Lazy) - case OVERRIDE => addFlag(Override) - case INLINE => addFlag(Inline) - case STATIC => addFlag(JavaStatic) - case OBJECT => addFlag(Module) - case TRAIT => addFlag(Trait) - case LOCAL => addFlag(Local) - case SYNTHETIC => addFlag(Synthetic) - case ARTIFACT => addFlag(Artifact) - case MUTABLE => addFlag(Mutable) - case LABEL => addFlag(Label) - case FIELDaccessor => addFlag(Accessor) - case CASEaccessor => addFlag(CaseAccessor) - case COVARIANT => addFlag(Covariant) - case CONTRAVARIANT => addFlag(Contravariant) - case SCALA2X => addFlag(Scala2x) - case DEFAULTparameterized => addFlag(DefaultParameterized) - case INSUPERCALL => addFlag(InSuperCall) - case STABLE => addFlag(Stable) - case PRIVATEqualified => - readByte() - privateWithin = readType().typeSymbol - case PROTECTEDqualified => - addFlag(Protected) - privateWithin = readType().typeSymbol - case ANNOTATION => - readByte() - val end = readEnd() - val sym = readType().typeSymbol - val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx)) - annots += Annotation.deferred(sym, _ => lazyAnnotTree.complete) - case _ => - assert(false, s"illegal modifier tag at $currentAddr") - } - } - (flags, annots.toList, privateWithin) - } - - /** Create symbols for the definitions in the statement sequence between - * current address and `end`. - * @return the largest subset of {NoInits, PureInterface} that a - * trait owning the indexed statements can have as flags. - */ - def indexStats(end: Addr)(implicit ctx: Context): FlagSet = { - var initsFlags = NoInitsInterface - while (currentAddr.index < end.index) { - nextByte match { - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM => - val sym = symbolAtCurrent() - skipTree() - if (sym.isTerm && !sym.is(MethodOrLazyOrDeferred)) - initsFlags = EmptyFlags - else if (sym.isClass || - sym.is(Method, butNot = Deferred) && !sym.isConstructor) - initsFlags &= NoInits - case IMPORT => - skipTree() - case PACKAGE => - processPackage { (pid, end) => implicit ctx => indexStats(end) } - case _ => - skipTree() - initsFlags = EmptyFlags - } - } - assert(currentAddr.index == end.index) - initsFlags - } - - /** Process package with given operation `op`. The operation takes as arguments - * - a `RefTree` representing the `pid` of the package, - * - an end address, - * - a context which has the processd package as owner - */ - def processPackage[T](op: (RefTree, Addr) => Context => T)(implicit ctx: Context): T = { - readByte() - val end = readEnd() - val pid = ref(readTermRef()).asInstanceOf[RefTree] - op(pid, end)(localContext(pid.symbol.moduleClass)) - } - - /** Create symbols the longest consecutive sequence of parameters with given - * `tag` starting at current address. - */ - def indexParams(tag: Int)(implicit ctx: Context) = - while (nextByte == tag) { - symbolAtCurrent() - skipTree() - } - - /** Create symbols for all type and value parameters of template starting - * at current address. - */ - def indexTemplateParams()(implicit ctx: Context) = { - assert(readByte() == TEMPLATE) - readEnd() - indexParams(TYPEPARAM) - indexParams(PARAM) - } - - /** If definition was already read by a completer, return the previously read tree - * or else read definition. - */ - def readIndexedDef()(implicit ctx: Context): Tree = treeAtAddr.remove(currentAddr) match { - case Some(tree) => skipTree(); tree - case none => readNewDef() - } - - private def readNewDef()(implicit ctx: Context): Tree = { - val start = currentAddr - val sym = symAtAddr(start) - val tag = readByte() - val end = readEnd() - - def readParamss(implicit ctx: Context): List[List[ValDef]] = { - collectWhile(nextByte == PARAMS) { - readByte() - readEnd() - readParams[ValDef](PARAM) - } - } - - def readRhs(implicit ctx: Context) = - if (noRhs(end)) EmptyTree - else readLater(end, rdr => ctx => rdr.readTerm()(ctx)) - - def localCtx = localContext(sym) - - def ValDef(tpt: Tree) = - ta.assignType(untpd.ValDef(sym.name.asTermName, tpt, readRhs(localCtx)), sym) - - def DefDef(tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree) = - ta.assignType( - untpd.DefDef( - sym.name.asTermName, tparams, vparamss, tpt, readRhs(localCtx)), - sym) - - def TypeDef(rhs: Tree) = - ta.assignType(untpd.TypeDef(sym.name.asTypeName, rhs), sym) - - def ta = ctx.typeAssigner - - val name = readName() - pickling.println(s"reading def of $name at $start") - val tree: MemberDef = tag match { - case DEFDEF => - val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) - val vparamss = readParamss(localCtx) - val tpt = readTpt() - val typeParams = tparams.map(_.symbol) - val valueParamss = ctx.normalizeIfConstructor( - vparamss.nestedMap(_.symbol), name == nme.CONSTRUCTOR) - val resType = ctx.effectiveResultType(sym, typeParams, tpt.tpe) - sym.info = ctx.methodType(typeParams, valueParamss, resType) - if (sym.isSetter && sym.accessedFieldOrGetter.is(ParamAccessor)) { - // reconstitute ParamAccessor flag of setters for var parameters, which is not pickled - sym.setFlag(ParamAccessor) - sym.resetFlag(Deferred) - } - DefDef(tparams, vparamss, tpt) - case VALDEF => - val tpt = readTpt() - sym.info = tpt.tpe - ValDef(tpt) - case TYPEDEF | TYPEPARAM => - if (sym.isClass) { - val companion = sym.scalacLinkedClass - - // Is the companion defined in the same Tasty file as `sym`? - // The only case to check here is if `sym` is a root. In this case - // `companion` might have been entered by the environment but it might - // be missing from the Tasty file. So we check explicitly for that. - def isCodefined = - roots.contains(companion.denot) == seenRoots.contains(companion) - if (companion.exists && isCodefined) { - import transform.SymUtils._ - if (sym is Flags.ModuleClass) sym.registerCompanionMethod(nme.COMPANION_CLASS_METHOD, companion) - else sym.registerCompanionMethod(nme.COMPANION_MODULE_METHOD, companion) - } - TypeDef(readTemplate(localCtx)) - } else { - val rhs = readTpt() - sym.info = rhs.tpe match { - case _: TypeBounds | _: ClassInfo => rhs.tpe - case _ => TypeAlias(rhs.tpe, sym.variance) - } - TypeDef(rhs) - } - case PARAM => - val tpt = readTpt() - if (noRhs(end)) { - sym.info = tpt.tpe - ValDef(tpt) - } - else { - sym.setFlag(Method) - sym.info = ExprType(tpt.tpe) - pickling.println(i"reading param alias $name -> $currentAddr") - DefDef(Nil, Nil, tpt) - } - } - val mods = - if (sym.annotations.isEmpty) untpd.EmptyModifiers - else untpd.Modifiers(annotations = sym.annotations.map(_.tree)) - tree.withMods(mods) - // record annotations in tree so that tree positions can be filled in. - // Note: Once the inline PR with its changes to positions is in, this should be - // no longer necessary. - goto(end) - setPos(start, tree) - } - - private def readTemplate(implicit ctx: Context): Template = { - val start = currentAddr - val cls = ctx.owner.asClass - def setClsInfo(parents: List[TypeRef], selfType: Type) = - cls.info = ClassInfo(cls.owner.thisType, cls, parents, cls.unforcedDecls, selfType) - val assumedSelfType = - if (cls.is(Module) && cls.owner.isClass) - TermRef.withSig(cls.owner.thisType, cls.name.sourceModuleName, Signature.NotAMethod) - else NoType - setClsInfo(Nil, assumedSelfType) - val localDummy = symbolAtCurrent() - assert(readByte() == TEMPLATE) - val end = readEnd() - val tparams = readIndexedParams[TypeDef](TYPEPARAM) - val vparams = readIndexedParams[ValDef](PARAM) - val parents = collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) { - nextByte match { - case APPLY | TYPEAPPLY => readTerm() - case _ => readTpt() - } - } - val parentRefs = ctx.normalizeToClassRefs(parents.map(_.tpe), cls, cls.unforcedDecls) - val self = - if (nextByte == SELFDEF) { - readByte() - untpd.ValDef(readName(), readTpt(), EmptyTree).withType(NoType) - } - else EmptyValDef - setClsInfo(parentRefs, if (self.isEmpty) NoType else self.tpt.tpe) - cls.setApplicableFlags(fork.indexStats(end)) - val constr = readIndexedDef().asInstanceOf[DefDef] - - def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree]): (List[Tree], List[Tree]) = - (tparams, stats) match { - case (tparam :: tparams1, (alias: TypeDef) :: stats1) - if tparam.name == alias.name.expandedName(cls) => - val (tas, stats2) = mergeTypeParamsAndAliases(tparams1, stats1) - (tparam :: alias :: tas, stats2) - case _ => - (tparams, stats) - } - - val lazyStats = readLater(end, rdr => implicit ctx => { - val stats0 = rdr.readIndexedStats(localDummy, end) - val (tparamsAndAliases, stats) = mergeTypeParamsAndAliases(tparams, stats0) - tparamsAndAliases ++ vparams ++ stats - }) - setPos(start, - untpd.Template(constr, parents, self, lazyStats) - .withType(localDummy.nonMemberTermRef)) - } - - def skipToplevel()(implicit ctx: Context): Unit= { - if (!isAtEnd) - nextByte match { - case IMPORT | PACKAGE => - skipTree() - skipToplevel() - case _ => - } - } - - def readTopLevel()(implicit ctx: Context): List[Tree] = { - @tailrec def read(acc: ListBuffer[Tree]): List[Tree] = nextByte match { - case IMPORT | PACKAGE => - acc += readIndexedStat(NoSymbol) - if (!isAtEnd) read(acc) else acc.toList - case _ => // top-level trees which are not imports or packages are not part of tree - acc.toList - } - read(new ListBuffer[tpd.Tree]) - } - - def readIndexedStat(exprOwner: Symbol)(implicit ctx: Context): Tree = nextByte match { - case TYPEDEF | VALDEF | DEFDEF => - readIndexedDef() - case IMPORT => - readImport() - case PACKAGE => - val start = currentAddr - processPackage { (pid, end) => implicit ctx => - setPos(start, PackageDef(pid, readIndexedStats(exprOwner, end)(ctx))) - } - case _ => - readTerm()(ctx.withOwner(exprOwner)) - } - - def readImport()(implicit ctx: Context): Tree = { - val start = currentAddr - readByte() - readEnd() - val expr = readTerm() - def readSelectors(): List[untpd.Tree] = nextByte match { - case IMPORTED => - val start = currentAddr - readByte() - val from = setPos(start, untpd.Ident(readName())) - nextByte match { - case RENAMED => - val start2 = currentAddr - readByte() - val to = setPos(start2, untpd.Ident(readName())) - untpd.Thicket(from, to) :: readSelectors() - case _ => - from :: readSelectors() - } - case _ => - Nil - } - setPos(start, Import(expr, readSelectors())) - } - - def readIndexedStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = - until(end)(readIndexedStat(exprOwner)) - - def readStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = { - fork.indexStats(end) - readIndexedStats(exprOwner, end) - } - - def readIndexedParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = - collectWhile(nextByte == tag) { readIndexedDef().asInstanceOf[T] } - - def readParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = { - fork.indexParams(tag) - readIndexedParams(tag) - } - -// ------ Reading trees ----------------------------------------------------- - - def readTerm()(implicit ctx: Context): Tree = { // TODO: rename to readTree - val start = currentAddr - val tag = readByte() - pickling.println(s"reading term ${astTagToString(tag)} at $start") - - def readPathTerm(): Tree = { - goto(start) - readType() match { - case path: TypeRef => TypeTree(path) - case path: TermRef => ref(path) - case path: ThisType => This(path.cls) - case path: ConstantType => Literal(path.value) - } - } - - def completeSelect(name: Name, tpf: Type => Type): Select = { - val localCtx = - if (name == nme.CONSTRUCTOR) ctx.addMode(Mode.InSuperCall) else ctx - val qual = readTerm()(localCtx) - val unshadowed = if (name.isShadowedName) name.revertShadowed else name - untpd.Select(qual, unshadowed).withType(tpf(qual.tpe.widenIfUnstable)) - } - - def readQualId(): (untpd.Ident, TypeRef) = { - val qual = readTerm().asInstanceOf[untpd.Ident] - (untpd.Ident(qual.name).withPos(qual.pos), qual.tpe.asInstanceOf[TypeRef]) - } - - def readSimpleTerm(): Tree = tag match { - case SHARED => - forkAt(readAddr()).readTerm() - case IDENT => - untpd.Ident(readName()).withType(readType()) - case IDENTtpt => - untpd.Ident(readName().toTypeName).withType(readType()) - case SELECT => - def readRest(name: Name, sig: Signature) = - completeSelect(name, TermRef.withSig(_, name.asTermName, sig)) - readNameSplitSig match { - case name: Name => readRest(name, Signature.NotAMethod) - case (name: Name, sig: Signature) => readRest(name, sig) - } - case SELECTtpt => - val name = readName().toTypeName - completeSelect(name, TypeRef(_, name)) - case QUALTHIS => - val (qual, tref) = readQualId() - untpd.This(qual).withType(ThisType.raw(tref)) - case NEW => - New(readTpt()) - case SINGLETONtpt => - SingletonTypeTree(readTerm()) - case BYNAMEtpt => - ByNameTypeTree(readTpt()) - case _ => - readPathTerm() - } - - def readLengthTerm(): Tree = { - val end = readEnd() - - def localNonClassCtx = { - val ctx1 = ctx.fresh.setNewScope - if (ctx.owner.isClass) ctx1.setOwner(ctx1.newLocalDummy(ctx.owner)) else ctx1 - } - - def readBlock(mkTree: (List[Tree], Tree) => Tree): Tree = { - val exprReader = fork - skipTree() - val localCtx = localNonClassCtx - val stats = readStats(ctx.owner, end)(localCtx) - val expr = exprReader.readTerm()(localCtx) - mkTree(stats, expr) - } - - val result = - (tag: @switch) match { - case SUPER => - val qual = readTerm() - val (mixId, mixTpe) = ifBefore(end)(readQualId(), (untpd.EmptyTypeIdent, NoType)) - tpd.Super(qual, mixId, ctx.mode.is(Mode.InSuperCall), mixTpe.typeSymbol) - case APPLY => - val fn = readTerm() - val isJava = fn.symbol.is(JavaDefined) - def readArg() = readTerm() match { - case SeqLiteral(elems, elemtpt) if isJava => - JavaSeqLiteral(elems, elemtpt) - case arg => arg - } - tpd.Apply(fn, until(end)(readArg())) - case TYPEAPPLY => - tpd.TypeApply(readTerm(), until(end)(readTpt())) - case TYPED => - val expr = readTerm() - val tpt = readTpt() - val expr1 = expr match { - case SeqLiteral(elems, elemtpt) if tpt.tpe.isRef(defn.ArrayClass) => - JavaSeqLiteral(elems, elemtpt) - case expr => expr - } - Typed(expr1, tpt) - case NAMEDARG => - NamedArg(readName(), readTerm()) - case ASSIGN => - Assign(readTerm(), readTerm()) - case BLOCK => - readBlock(Block) - case INLINED => - val call = readTerm() - readBlock((defs, expr) => Inlined(call, defs.asInstanceOf[List[MemberDef]], expr)) - case IF => - If(readTerm(), readTerm(), readTerm()) - case LAMBDA => - val meth = readTerm() - val tpt = ifBefore(end)(readTpt(), EmptyTree) - Closure(Nil, meth, tpt) - case MATCH => - Match(readTerm(), readCases(end)) - case RETURN => - val from = readSymRef() - val expr = ifBefore(end)(readTerm(), EmptyTree) - Return(expr, Ident(from.termRef)) - case TRY => - Try(readTerm(), readCases(end), ifBefore(end)(readTerm(), EmptyTree)) - case REPEATED => - val elemtpt = readTpt() - SeqLiteral(until(end)(readTerm()), elemtpt) - case BIND => - val name = readName() - val info = readType() - val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, info) - registerSym(start, sym) - Bind(sym, readTerm()) - case ALTERNATIVE => - Alternative(until(end)(readTerm())) - case UNAPPLY => - val fn = readTerm() - val implicitArgs = - collectWhile(nextByte == IMPLICITarg) { - readByte() - readTerm() - } - val patType = readType() - val argPats = until(end)(readTerm()) - UnApply(fn, implicitArgs, argPats, patType) - case REFINEDtpt => - val refineCls = ctx.newCompleteClassSymbol( - ctx.owner, tpnme.REFINE_CLASS, Fresh, parents = Nil) - typeAtAddr(start) = refineCls.typeRef - val parent = readTpt() - val refinements = readStats(refineCls, end)(localContext(refineCls)) - RefinedTypeTree(parent, refinements, refineCls) - case APPLIEDtpt => - AppliedTypeTree(readTpt(), until(end)(readTpt())) - case ANDtpt => - AndTypeTree(readTpt(), readTpt()) - case ORtpt => - OrTypeTree(readTpt(), readTpt()) - case ANNOTATEDtpt => - Annotated(readTpt(), readTerm()) - case POLYtpt => - val localCtx = localNonClassCtx - val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) - val body = readTpt()(localCtx) - PolyTypeTree(tparams, body) - case TYPEBOUNDStpt => - TypeBoundsTree(readTpt(), readTpt()) - case _ => - readPathTerm() - } - assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") - result - } - - val tree = if (tag < firstLengthTreeTag) readSimpleTerm() else readLengthTerm() - tree.overwriteType(tree.tpe.simplified) - setPos(start, tree) - } - - def readTpt()(implicit ctx: Context) = - if (isTypeTreeTag(nextUnsharedTag)) readTerm() - else { - val start = currentAddr - val tp = readType() - if (tp.exists) setPos(start, TypeTree(tp)) else EmptyTree - } - - def readCases(end: Addr)(implicit ctx: Context): List[CaseDef] = - collectWhile(nextByte == CASEDEF && currentAddr != end) { readCase()(ctx.fresh.setNewScope) } - - def readCase()(implicit ctx: Context): CaseDef = { - val start = currentAddr - readByte() - val end = readEnd() - val pat = readTerm() - val rhs = readTerm() - val guard = ifBefore(end)(readTerm(), EmptyTree) - setPos(start, CaseDef(pat, guard, rhs)) - } - - def readLater[T <: AnyRef](end: Addr, op: TreeReader => Context => T): Trees.Lazy[T] = { - val localReader = fork - goto(end) - new LazyReader(localReader, op) - } - -// ------ Setting positions ------------------------------------------------ - - /** Set position of `tree` at given `addr`. */ - def setPos[T <: untpd.Tree](addr: Addr, tree: T)(implicit ctx: Context): tree.type = - if (ctx.mode.is(Mode.ReadPositions)) { - posUnpicklerOpt match { - case Some(posUnpickler) => - //println(i"setPos $tree / ${tree.getClass} at $addr to ${posUnpickler.posAt(addr)}") - val pos = posUnpickler.posAt(addr) - if (pos.exists) tree.setPosUnchecked(pos) - tree - case _ => - //println(i"no pos $tree") - tree - } - } - else tree - } - - class LazyReader[T <: AnyRef](reader: TreeReader, op: TreeReader => Context => T) extends Trees.Lazy[T] { - def complete(implicit ctx: Context): T = { - pickling.println(i"starting to read at ${reader.reader.currentAddr}") - op(reader)(ctx.addMode(Mode.AllowDependentFunctions).withPhaseNoLater(ctx.picklerPhase)) - } - } - - class LazyAnnotationReader(sym: Symbol, reader: TreeReader) extends LazyAnnotation(sym) { - def complete(implicit ctx: Context) = { - reader.readTerm()(ctx.withPhaseNoLater(ctx.picklerPhase)) - } - } - - /** A lazy datastructure that records how definitions are nested in TASTY data. - * The structure is lazy because it needs to be computed only for forward references - * to symbols that happen before the referenced symbol is created (see `symbolAt`). - * Such forward references are rare. - * - * @param addr The address of tree representing an owning definition, NoAddr for root tree - * @param tag The tag at `addr`. Used to determine which subtrees to scan for children - * (i.e. if `tag` is template, don't scan member defs, as these belong already - * to enclosing class). - * @param reader The reader to be used for scanning for children - * @param end The end of the owning definition - */ - class OwnerTree(val addr: Addr, tag: Int, reader: TreeReader, val end: Addr) { - - /** All definitions that have the definition at `addr` as closest enclosing definition */ - lazy val children: List[OwnerTree] = { - val buf = new ListBuffer[OwnerTree] - reader.scanTrees(buf, end, if (tag == TEMPLATE) NoMemberDefs else AllDefs) - buf.toList - } - - /** Find the owner of definition at `addr` */ - def findOwner(addr: Addr)(implicit ctx: Context): Symbol = { - def search(cs: List[OwnerTree], current: Symbol): Symbol = - try cs match { - case ot :: cs1 => - if (ot.addr.index == addr.index) - current - else if (ot.addr.index < addr.index && addr.index < ot.end.index) - search(ot.children, reader.symbolAt(ot.addr)) - else - search(cs1, current) - case Nil => - throw new TreeWithoutOwner - } - catch { - case ex: TreeWithoutOwner => - println(i"no owner for $addr among $cs") // DEBUG - throw ex - } - search(children, NoSymbol) - } - - override def toString = s"OwnerTree(${addr.index}, ${end.index}" - } -} - -object TreeUnpickler { - - /** An enumeration indicating which subtrees should be added to an OwnerTree. */ - type MemberDefMode = Int - final val MemberDefsOnly = 0 // add only member defs; skip other statements - final val NoMemberDefs = 1 // add only statements that are not member defs - final val AllDefs = 2 // add everything - - class TreeWithoutOwner extends Exception -} - - diff --git a/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala b/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala deleted file mode 100644 index 17fef3852..000000000 --- a/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala +++ /dev/null @@ -1,299 +0,0 @@ -package dotty.tools -package dotc -package core -package unpickleScala2 - -import Flags._ - -/** Variable length byte arrays, with methods for basic pickling and unpickling. - * - * @param data The initial buffer - * @param from The first index where defined data are found - * @param to The first index where new data can be written - */ -class PickleBuffer(data: Array[Byte], from: Int, to: Int) { - - var bytes = data - var readIndex = from - var writeIndex = to - - /** Double bytes array */ - private def dble(): Unit = { - val bytes1 = new Array[Byte](bytes.length * 2) - Array.copy(bytes, 0, bytes1, 0, writeIndex) - bytes = bytes1 - } - - def ensureCapacity(capacity: Int) = - while (bytes.length < writeIndex + capacity) dble() - - // -- Basic output routines -------------------------------------------- - - /** Write a byte of data */ - def writeByte(b: Int): Unit = { - if (writeIndex == bytes.length) dble() - bytes(writeIndex) = b.toByte - writeIndex += 1 - } - - /** Write a natural number in big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def writeNat(x: Int): Unit = - writeLongNat(x.toLong & 0x00000000FFFFFFFFL) - - /** - * Like writeNat, but for longs. This is not the same as - * writeLong, which writes in base 256. Note that the - * binary representation of LongNat is identical to Nat - * if the long value is in the range Int.MIN_VALUE to - * Int.MAX_VALUE. - */ - def writeLongNat(x: Long): Unit = { - def writeNatPrefix(x: Long): Unit = { - val y = x >>> 7 - if (y != 0L) writeNatPrefix(y) - writeByte(((x & 0x7f) | 0x80).toInt) - } - val y = x >>> 7 - if (y != 0L) writeNatPrefix(y) - writeByte((x & 0x7f).toInt) - } - - /** Write a natural number <code>x</code> at position <code>pos</code>. - * If number is more than one byte, shift rest of array to make space. - * - * @param pos ... - * @param x ... - */ - def patchNat(pos: Int, x: Int): Unit = { - def patchNatPrefix(x: Int): Unit = { - writeByte(0) - Array.copy(bytes, pos, bytes, pos + 1, writeIndex - (pos + 1)) - bytes(pos) = ((x & 0x7f) | 0x80).toByte - val y = x >>> 7 - if (y != 0) patchNatPrefix(y) - } - bytes(pos) = (x & 0x7f).toByte - val y = x >>> 7 - if (y != 0) patchNatPrefix(y) - } - - /** Write a long number <code>x</code> in signed big endian format, base 256. - * - * @param x The long number to be written. - */ - def writeLong(x: Long): Unit = { - val y = x >> 8 - val z = x & 0xff - if (-y != (z >> 7)) writeLong(y) - writeByte(z.toInt) - } - - // -- Basic input routines -------------------------------------------- - - /** Peek at the current byte without moving the read index */ - def peekByte(): Int = bytes(readIndex) - - /** Read a byte */ - def readByte(): Int = { - val x = bytes(readIndex); readIndex += 1; x - } - - /** Read a natural number in big endian format, base 128. - * All but the last digits have bit 0x80 set.*/ - def readNat(): Int = readLongNat().toInt - - def readLongNat(): Long = { - var b = 0L - var x = 0L - do { - b = readByte() - x = (x << 7) + (b & 0x7f) - } while ((b & 0x80) != 0L) - x - } - - /** Read a long number in signed big endian format, base 256. */ - def readLong(len: Int): Long = { - var x = 0L - var i = 0 - while (i < len) { - x = (x << 8) + (readByte() & 0xff) - i += 1 - } - val leading = 64 - (len << 3) - x << leading >> leading - } - - /** Returns the buffer as a sequence of (Int, Array[Byte]) representing - * (tag, data) of the individual entries. Saves and restores buffer state. - */ - - def toIndexedSeq: IndexedSeq[(Int, Array[Byte])] = { - val saved = readIndex - readIndex = 0 - readNat() ; readNat() // discarding version - val result = new Array[(Int, Array[Byte])](readNat()) - - result.indices foreach { index => - val tag = readNat() - val len = readNat() - val bytes = data.slice(readIndex, len + readIndex) - readIndex += len - - result(index) = tag -> bytes - } - - readIndex = saved - result.toIndexedSeq - } - - /** Perform operation <code>op</code> until the condition - * <code>readIndex == end</code> is satisfied. - * Concatenate results into a list. - * - * @param end ... - * @param op ... - * @return ... - */ - def until[T](end: Int, op: () => T): List[T] = - if (readIndex == end) List() else op() :: until(end, op) - - /** Perform operation <code>op</code> the number of - * times specified. Concatenate the results into a list. - */ - def times[T](n: Int, op: ()=>T): List[T] = - if (n == 0) List() else op() :: times(n-1, op) - - /** Pickle = majorVersion_Nat minorVersion_Nat nbEntries_Nat {Entry} - * Entry = type_Nat length_Nat [actual entries] - * - * Assumes that the ..Version_Nat are already consumed. - * - * @return an array mapping entry numbers to locations in - * the byte array where the entries start. - */ - def createIndex: Array[Int] = { - val index = new Array[Int](readNat()) // nbEntries_Nat - for (i <- 0 until index.length) { - index(i) = readIndex - readByte() // skip type_Nat - readIndex = readNat() + readIndex // read length_Nat, jump to next entry - } - index - } -} - -object PickleBuffer { - - private final val ScalaFlagEnd = 48 - private final val ChunkBits = 8 - private final val ChunkSize = 1 << ChunkBits - private type FlagMap = Array[Array[Long]] - - private val (scalaTermFlagMap, scalaTypeFlagMap) = { - import scala.reflect.internal.Flags._ - - // The following vals are copy-pasted from reflect.internal.Flags. - // They are unfortunately private there, so we cannot get at them directly. - // Using the public method pickledToRawFlags instead looks unattractive - // because of performance. - val IMPLICIT_PKL = (1 << 0) - val FINAL_PKL = (1 << 1) - val PRIVATE_PKL = (1 << 2) - val PROTECTED_PKL = (1 << 3) - val SEALED_PKL = (1 << 4) - val OVERRIDE_PKL = (1 << 5) - val CASE_PKL = (1 << 6) - val ABSTRACT_PKL = (1 << 7) - val DEFERRED_PKL = (1 << 8) - val METHOD_PKL = (1 << 9) - val MODULE_PKL = (1 << 10) - val INTERFACE_PKL = (1 << 11) - - val corr = Map( - PROTECTED_PKL -> Protected, - OVERRIDE_PKL -> Override, - PRIVATE_PKL -> Private, - ABSTRACT_PKL -> Abstract, - DEFERRED_PKL -> Deferred, - FINAL_PKL -> Final, - METHOD_PKL -> Method, - INTERFACE_PKL -> NoInitsInterface, - MODULE_PKL -> (Module | Lazy, Module), - IMPLICIT_PKL -> Implicit, - SEALED_PKL -> Sealed, - CASE_PKL -> Case, - MUTABLE -> Mutable, - PARAM -> Param, - PACKAGE -> Package, - MACRO -> Macro, - BYNAMEPARAM -> (Method, Covariant), - LABEL -> (Label, Contravariant), - ABSOVERRIDE -> AbsOverride, - LOCAL -> Local, - JAVA -> JavaDefined, - SYNTHETIC -> Synthetic, - STABLE -> Stable, - STATIC -> JavaStatic, - CASEACCESSOR -> CaseAccessor, - DEFAULTPARAM -> (DefaultParameterized, Trait), - BRIDGE -> Bridge, - ACCESSOR -> Accessor, - SUPERACCESSOR -> SuperAccessor, - PARAMACCESSOR -> ParamAccessor, - MODULEVAR -> Scala2ModuleVar, - LAZY -> Lazy, - MIXEDIN -> (MixedIn, Scala2Existential), - EXPANDEDNAME -> ExpandedName, - IMPLCLASS -> (Scala2PreSuper, ImplClass), - SPECIALIZED -> Specialized, - VBRIDGE -> VBridge, - VARARGS -> JavaVarargs, - ENUM -> Enum) - - // generate initial maps from Scala flags to Dotty flags - val termMap, typeMap = new Array[Long](64) - for (idx <- 0 until ScalaFlagEnd) - corr get (1L << idx) match { - case Some((termFlag: FlagSet, typeFlag: FlagSet)) => - termMap(idx) |= termFlag.bits - typeMap(idx) |= typeFlag.bits - case Some(commonFlag: FlagSet) => - termMap(idx) |= commonFlag.toTermFlags.bits - typeMap(idx) |= commonFlag.toTypeFlags.bits - case _ => - } - - // Convert map so that it maps chunks of ChunkBits size at once - // instead of single bits. - def chunkMap(xs: Array[Long]): FlagMap = { - val chunked = Array.ofDim[Long]( - (xs.length + ChunkBits - 1) / ChunkBits, ChunkSize) - for (i <- 0 until chunked.length) - for (j <- 0 until ChunkSize) - for (k <- 0 until ChunkBits) - if ((j & (1 << k)) != 0) - chunked(i)(j) |= xs(i * ChunkBits + k) - chunked - } - - (chunkMap(termMap), chunkMap(typeMap)) - } - - def unpickleScalaFlags(sflags: Long, isType: Boolean): FlagSet = { - val map: FlagMap = if (isType) scalaTypeFlagMap else scalaTermFlagMap - val shift = ChunkBits - val mask = ChunkSize - 1 - assert(6 * ChunkBits == ScalaFlagEnd) - FlagSet( - map(0)((sflags >>> (shift * 0)).toInt & mask) | - map(1)((sflags >>> (shift * 1)).toInt & mask) | - map(2)((sflags >>> (shift * 2)).toInt & mask) | - map(3)((sflags >>> (shift * 3)).toInt & mask) | - map(4)((sflags >>> (shift * 4)).toInt & mask) | - map(5)((sflags >>> (shift * 5)).toInt & mask) - ) - } -} diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala deleted file mode 100644 index b01f6cc6a..000000000 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ /dev/null @@ -1,1260 +0,0 @@ -package dotty.tools -package dotc -package core -package unpickleScala2 - -import java.io.IOException -import java.lang.Float.intBitsToFloat -import java.lang.Double.longBitsToDouble - -import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ -import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._ -import dotty.tools.dotc.typer.ProtoTypes.{FunProtoTyped, FunProto} -import util.Positions._ -import dotty.tools.dotc.ast.{tpd, Trees, untpd}, ast.tpd._ -import ast.untpd.Modifiers -import printing.Texts._ -import printing.Printer -import io.AbstractFile -import util.common._ -import typer.Checking.checkNonCyclic -import PickleBuffer._ -import scala.reflect.internal.pickling.PickleFormat._ -import Decorators._ -import TypeApplications._ -import classfile.ClassfileParser -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.ListBuffer -import scala.annotation.switch - -object Scala2Unpickler { - - /** Exception thrown if classfile is corrupted */ - class BadSignature(msg: String) extends RuntimeException(msg) - - case class TempPolyType(tparams: List[TypeSymbol], tpe: Type) extends UncachedGroundType { - override def fallbackToText(printer: Printer): Text = - "[" ~ printer.dclsText(tparams, ", ") ~ "]" ~ printer.toText(tpe) - } - - /** Temporary type for classinfos, will be decomposed on completion of the class */ - case class TempClassInfoType(parentTypes: List[Type], decls: Scope, clazz: Symbol) extends UncachedGroundType - - /** Convert temp poly type to poly type and leave other types alone. */ - def translateTempPoly(tp: Type)(implicit ctx: Context): Type = tp match { - case TempPolyType(tparams, restpe) => restpe.LambdaAbstract(tparams) - case tp => tp - } - - def addConstructorTypeParams(denot: SymDenotation)(implicit ctx: Context) = { - assert(denot.isConstructor) - denot.info = denot.info.LambdaAbstract(denot.owner.typeParams) - } - - /** Convert array parameters denoting a repeated parameter of a Java method - * to `RepeatedParamClass` types. - */ - def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match { - case tp @ MethodType(paramNames, paramTypes) => - val lastArg = paramTypes.last - assert(lastArg isRef defn.ArrayClass) - val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass) - val elemtp = elemtp0 match { - case AndType(t1, t2) if t1.typeSymbol.isAbstractType && (t2 isRef defn.ObjectClass) => - t1 // drop intersection with Object for abstract types in varargs. UnCurry can handle them. - case _ => - elemtp0 - } - tp.derivedMethodType( - paramNames, - paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp), - tp.resultType) - case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, arrayToRepeated(tp.resultType)) - } - - def ensureConstructor(cls: ClassSymbol, scope: Scope)(implicit ctx: Context) = - if (scope.lookup(nme.CONSTRUCTOR) == NoSymbol) { - val constr = ctx.newDefaultConstructor(cls) - addConstructorTypeParams(constr) - cls.enter(constr, scope) - } - - def setClassInfo(denot: ClassDenotation, info: Type, selfInfo: Type = NoType)(implicit ctx: Context): Unit = { - val cls = denot.classSymbol - val (tparams, TempClassInfoType(parents, decls, clazz)) = info match { - case TempPolyType(tps, cinfo) => (tps, cinfo) - case cinfo => (Nil, cinfo) - } - val ost = - if ((selfInfo eq NoType) && (denot is ModuleClass) && denot.sourceModule.exists) - // it seems sometimes the source module does not exist for a module class. - // An example is `scala.reflect.internal.Trees.Template$. Without the - // `denot.sourceModule.exists` provision i859.scala crashes in the backend. - denot.owner.thisType select denot.sourceModule - else selfInfo - val tempInfo = new TempClassInfo(denot.owner.thisType, denot.classSymbol, decls, ost) - denot.info = tempInfo // first rough info to avoid CyclicReferences - var parentRefs = ctx.normalizeToClassRefs(parents, cls, decls) - if (parentRefs.isEmpty) parentRefs = defn.ObjectType :: Nil - for (tparam <- tparams) { - val tsym = decls.lookup(tparam.name) - if (tsym.exists) tsym.setFlag(TypeParam) - else denot.enter(tparam, decls) - } - if (!(denot.flagsUNSAFE is JavaModule)) ensureConstructor(denot.symbol.asClass, decls) - - val scalacCompanion = denot.classSymbol.scalacLinkedClass - - def registerCompanionPair(module: Symbol, claz: Symbol) = { - import transform.SymUtils._ - module.registerCompanionMethod(nme.COMPANION_CLASS_METHOD, claz) - if (claz.isClass) { - claz.registerCompanionMethod(nme.COMPANION_MODULE_METHOD, module) - } - } - - if (denot.flagsUNSAFE is Module) { - registerCompanionPair(denot.classSymbol, scalacCompanion) - } else { - registerCompanionPair(scalacCompanion, denot.classSymbol) - } - - tempInfo.finalize(denot, parentRefs) // install final info, except possibly for typeparams ordering - denot.ensureTypeParamsInCorrectOrder() - } -} - -/** Unpickle symbol table information descending from a class and/or module root - * from an array of bytes. - * @param bytes bytearray from which we unpickle - * @param classroot the top-level class which is unpickled, or NoSymbol if inapplicable - * @param moduleroot the top-level module class which is unpickled, or NoSymbol if inapplicable - * @param filename filename associated with bytearray, only used for error messages - */ -class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: ClassDenotation)(ictx: Context) - extends PickleBuffer(bytes, 0, -1) with ClassfileParser.Embedded { - - def showPickled() = { - atReadPos(0, () => { - println(s"classRoot = ${classRoot.debugString}, moduleClassRoot = ${moduleClassRoot.debugString}") - util.ShowPickled.printFile(this) - }) - } - - // print("unpickling "); showPickled() // !!! DEBUG - - import Scala2Unpickler._ - - val moduleRoot = moduleClassRoot.sourceModule(ictx).denot(ictx) - assert(moduleRoot.isTerm) - - checkVersion(ictx) - - private val loadingMirror = defn(ictx) // was: mirrorThatLoaded(classRoot) - - /** A map from entry numbers to array offsets */ - private val index = createIndex - - /** A map from entry numbers to symbols, types, or annotations */ - private val entries = new Array[AnyRef](index.length) - - /** A map from symbols to their associated `decls` scopes */ - private val symScopes = mutable.AnyRefMap[Symbol, Scope]() - - protected def errorBadSignature(msg: String, original: Option[RuntimeException] = None)(implicit ctx: Context) = { - val ex = new BadSignature( - i"""error reading Scala signature of $classRoot from $source: - |error occurred at position $readIndex: $msg""") - if (ctx.debug || true) original.getOrElse(ex).printStackTrace() // temporarily enable printing of original failure signature to debug failing builds - throw ex - } - - protected def handleRuntimeException(ex: RuntimeException)(implicit ctx: Context) = ex match { - case ex: BadSignature => throw ex - case _ => errorBadSignature(s"a runtime exception occurred: $ex", Some(ex)) - } - - def run()(implicit ctx: Context) = - try { - var i = 0 - while (i < index.length) { - if (entries(i) == null && isSymbolEntry(i)) { - val savedIndex = readIndex - readIndex = index(i) - val sym = readSymbol() - entries(i) = sym - sym.infoOrCompleter match { - case info: ClassUnpickler => info.init() - case _ => - } - readIndex = savedIndex - } - i += 1 - } - // read children last, fix for #3951 - i = 0 - while (i < index.length) { - if (entries(i) == null) { - if (isSymbolAnnotationEntry(i)) { - val savedIndex = readIndex - readIndex = index(i) - readSymbolAnnotation() - readIndex = savedIndex - } else if (isChildrenEntry(i)) { - val savedIndex = readIndex - readIndex = index(i) - readChildren() - readIndex = savedIndex - } - } - i += 1 - } - } catch { - case ex: RuntimeException => handleRuntimeException(ex) - } - - def source(implicit ctx: Context): AbstractFile = { - val f = classRoot.symbol.associatedFile - if (f != null) f else moduleClassRoot.symbol.associatedFile - } - - private def checkVersion(implicit ctx: Context): Unit = { - val major = readNat() - val minor = readNat() - if (major != MajorVersion || minor > MinorVersion) - throw new IOException("Scala signature " + classRoot.fullName.decode + - " has wrong version\n expected: " + - MajorVersion + "." + MinorVersion + - "\n found: " + major + "." + minor + - " in " + source) - } - - /** The `decls` scope associated with given symbol */ - protected def symScope(sym: Symbol) = symScopes.getOrElseUpdate(sym, newScope) - - /** Does entry represent an (internal) symbol */ - protected def isSymbolEntry(i: Int)(implicit ctx: Context): Boolean = { - val tag = bytes(index(i)).toInt - (firstSymTag <= tag && tag <= lastSymTag && - (tag != CLASSsym || !isRefinementSymbolEntry(i))) - } - - /** Does entry represent an (internal or external) symbol */ - protected def isSymbolRef(i: Int): Boolean = { - val tag = bytes(index(i)) - (firstSymTag <= tag && tag <= lastExtSymTag) - } - - /** Does entry represent a name? */ - protected def isNameEntry(i: Int): Boolean = { - val tag = bytes(index(i)).toInt - tag == TERMname || tag == TYPEname - } - - /** Does entry represent a symbol annotation? */ - protected def isSymbolAnnotationEntry(i: Int): Boolean = { - val tag = bytes(index(i)).toInt - tag == SYMANNOT - } - - /** Does the entry represent children of a symbol? */ - protected def isChildrenEntry(i: Int): Boolean = { - val tag = bytes(index(i)).toInt - tag == CHILDREN - } - - /** Does entry represent a refinement symbol? - * pre: Entry is a class symbol - */ - protected def isRefinementSymbolEntry(i: Int)(implicit ctx: Context): Boolean = { - val savedIndex = readIndex - readIndex = index(i) - val tag = readByte().toInt - assert(tag == CLASSsym) - - readNat(); // read length - val result = readNameRef() == tpnme.REFINE_CLASS - readIndex = savedIndex - result - } - - protected def isRefinementClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name == tpnme.REFINE_CLASS - - protected def isLocal(sym: Symbol)(implicit ctx: Context) = isUnpickleRoot(sym.topLevelClass) - - protected def isUnpickleRoot(sym: Symbol)(implicit ctx: Context) = { - val d = sym.denot - d == moduleRoot || d == moduleClassRoot || d == classRoot - } - - /** If entry at <code>i</code> is undefined, define it by performing - * operation <code>op</code> with <code>readIndex at start of i'th - * entry. Restore <code>readIndex</code> afterwards. - */ - protected def at[T <: AnyRef](i: Int, op: () => T): T = { - var r = entries(i) - if (r eq null) { - r = atReadPos(index(i), op) - assert(entries(i) eq null, entries(i)) - entries(i) = r - } - r.asInstanceOf[T] - } - - protected def atReadPos[T](start: Int, op: () => T): T = { - val savedIndex = readIndex - readIndex = start - try op() - finally readIndex = savedIndex - } - - /** Read a name */ - protected def readName()(implicit ctx: Context): Name = { - val tag = readByte() - val len = readNat() - tag match { - case TERMname => termName(bytes, readIndex, len) - case TYPEname => typeName(bytes, readIndex, len) - case _ => errorBadSignature("bad name tag: " + tag) - } - } - protected def readTermName()(implicit ctx: Context): TermName = readName().toTermName - protected def readTypeName()(implicit ctx: Context): TypeName = readName().toTypeName - - /** Read a symbol */ - protected def readSymbol()(implicit ctx: Context): Symbol = readDisambiguatedSymbol(alwaysTrue)() - - /** Read a symbol, with possible disambiguation */ - protected def readDisambiguatedSymbol(p: Symbol => Boolean)()(implicit ctx: Context): Symbol = { - val start = indexCoord(readIndex) - val tag = readByte() - val end = readNat() + readIndex - def atEnd = readIndex == end - - def readExtSymbol(): Symbol = { - val name = readNameRef() - val owner = if (atEnd) loadingMirror.RootClass else readSymbolRef() - - def adjust(denot: Denotation) = { - val denot1 = denot.disambiguate(d => p(d.symbol)) - val sym = denot1.symbol - if (denot.exists && !denot1.exists) { // !!!DEBUG - val alts = denot.alternatives map (d => d + ":" + d.info + "/" + d.signature) - System.err.println(s"!!! disambiguation failure: $alts") - val members = denot.alternatives.head.symbol.owner.info.decls.toList map (d => d + ":" + d.info + "/" + d.signature) - System.err.println(s"!!! all members: $members") - } - if (tag == EXTref) sym else sym.moduleClass - } - - def fromName(name: Name): Symbol = name.toTermName match { - case nme.ROOT => loadingMirror.RootClass - case nme.ROOTPKG => loadingMirror.RootPackage - case _ => - def declIn(owner: Symbol) = adjust(owner.info.decl(name)) - val sym = declIn(owner) - if (sym.exists || owner.ne(defn.ObjectClass)) sym else declIn(defn.AnyClass) - } - - def slowSearch(name: Name): Symbol = - owner.info.decls.find(_.name == name).getOrElse(NoSymbol) - - def nestedObjectSymbol: Symbol = { - // If the owner is overloaded (i.e. a method), it's not possible to select the - // right member, so return NoSymbol. This can only happen when unpickling a tree. - // the "case Apply" in readTree() takes care of selecting the correct alternative - // after parsing the arguments. - //if (owner.isOverloaded) - // return NoSymbol - - if (tag == EXTMODCLASSref) { - val module = owner.info.decl(name.toTermName).suchThat(_ is Module) - module.info // force it, as completer does not yet point to module class. - module.symbol.moduleClass - - /* was: - val moduleVar = owner.info.decl(name.toTermName.moduleVarName).symbol - if (moduleVar.isLazyAccessor) - return moduleVar.lazyAccessor.lazyAccessor - */ - } else NoSymbol - } - - // println(s"read ext symbol $name from ${owner.denot.debugString} in ${classRoot.debugString}") // !!! DEBUG - - // (1) Try name. - fromName(name) orElse { - // (2) Try with expanded name. Can happen if references to private - // symbols are read from outside: for instance when checking the children - // of a class. See #1722. - fromName(name.toTermName.expandedName(owner)) orElse { - // (3) Try as a nested object symbol. - nestedObjectSymbol orElse { - // (4) Call the mirror's "missing" hook. - adjust(ctx.base.missingHook(owner, name)) orElse { - // println(owner.info.decls.toList.map(_.debugString).mkString("\n ")) // !!! DEBUG - // } - // (5) Create a stub symbol to defer hard failure a little longer. - System.err.println(i"***** missing reference, looking for $name in $owner") - System.err.println(i"decls = ${owner.info.decls}") - owner.info.decls.checkConsistent() - if (slowSearch(name).exists) - System.err.println(i"**** slow search found: ${slowSearch(name)}") - if (ctx.debug) Thread.dumpStack() - ctx.newStubSymbol(owner, name, source) - } - } - } - } - } - - tag match { - case NONEsym => return NoSymbol - case EXTref | EXTMODCLASSref => return readExtSymbol() - case _ => - } - - // symbols that were pickled with Pickler.writeSymInfo - val nameref = readNat() - val name0 = at(nameref, readName) - val owner = readSymbolRef() - - var flags = unpickleScalaFlags(readLongNat(), name0.isTypeName) - if (flags is DefaultParameter) { - // DefaultParameterized flag now on method, not parameter - //assert(flags is Param, s"$name0 in $owner") - flags = flags &~ DefaultParameterized - owner.setFlag(DefaultParameterized) - } - - val name1 = name0.adjustIfModuleClass(flags) - val name = if (name1 == nme.TRAIT_CONSTRUCTOR) nme.CONSTRUCTOR else name1 - - def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) && !(flags is ModuleClass) - def isModuleClassRoot = (name == moduleClassRoot.name) && (owner == moduleClassRoot.owner) && (flags is Module) - def isModuleRoot = (name == moduleClassRoot.name.sourceModuleName) && (owner == moduleClassRoot.owner) && (flags is Module) - - //if (isClassRoot) println(s"classRoot of $classRoot found at $readIndex, flags = $flags") // !!! DEBUG - //if (isModuleRoot) println(s"moduleRoot of $moduleRoot found at $readIndex, flags = $flags") // !!! DEBUG - //if (isModuleClassRoot) println(s"moduleClassRoot of $moduleClassRoot found at $readIndex, flags = $flags") // !!! DEBUG - - def completeRoot(denot: ClassDenotation, completer: LazyType): Symbol = { - denot.setFlag(flags) - denot.resetFlag(Touched) // allow one more completion - denot.info = completer - denot.symbol - } - - def finishSym(sym: Symbol): Symbol = { - if (sym.isClass) sym.setFlag(Scala2x) - val owner = sym.owner - if (owner.isClass && - !( isUnpickleRoot(sym) - || (sym is Scala2Existential) - || isRefinementClass(sym) - ) - ) - owner.asClass.enter(sym, symScope(owner)) - else if (isRefinementClass(owner)) - symScope(owner).openForMutations.enter(sym) - sym - } - - finishSym(tag match { - case TYPEsym | ALIASsym => - var name1 = name.asTypeName - var flags1 = flags - if (flags is TypeParam) { - name1 = name1.expandedName(owner) - flags1 |= owner.typeParamCreationFlags | ExpandedName - } - ctx.newSymbol(owner, name1, flags1, localMemberUnpickler, coord = start) - case CLASSsym => - var infoRef = readNat() - if (isSymbolRef(infoRef)) infoRef = readNat() - if (isClassRoot) - completeRoot( - classRoot, rootClassUnpickler(start, classRoot.symbol, NoSymbol, infoRef)) - else if (isModuleClassRoot) - completeRoot( - moduleClassRoot, rootClassUnpickler(start, moduleClassRoot.symbol, moduleClassRoot.sourceModule, infoRef)) - else if (name == tpnme.REFINE_CLASS) - // create a type alias instead - ctx.newSymbol(owner, name, flags, localMemberUnpickler, coord = start) - else { - def completer(cls: Symbol) = { - val unpickler = new ClassUnpickler(infoRef) withDecls symScope(cls) - if (flags is ModuleClass) - unpickler withSourceModule (implicit ctx => - cls.owner.info.decls.lookup(cls.name.sourceModuleName) - .suchThat(_ is Module).symbol) - else unpickler - } - ctx.newClassSymbol(owner, name.asTypeName, flags, completer, coord = start) - } - case VALsym => - ctx.newSymbol(owner, name.asTermName, flags, localMemberUnpickler, coord = start) - case MODULEsym => - if (isModuleRoot) { - moduleRoot setFlag flags - moduleRoot.symbol - } else ctx.newSymbol(owner, name.asTermName, flags, - new LocalUnpickler() withModuleClass(implicit ctx => - owner.info.decls.lookup(name.moduleClassName) - .suchThat(_ is Module).symbol) - , coord = start) - case _ => - errorBadSignature("bad symbol tag: " + tag) - }) - } - - class LocalUnpickler extends LazyType { - def startCoord(denot: SymDenotation): Coord = denot.symbol.coord - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = try { - def parseToCompletion(denot: SymDenotation)(implicit ctx: Context) = { - val tag = readByte() - val end = readNat() + readIndex - def atEnd = readIndex == end - val unusedNameref = readNat() - val unusedOwnerref = readNat() - val unusedFlags = readLongNat() - var inforef = readNat() - denot.privateWithin = - if (!isSymbolRef(inforef)) NoSymbol - else { - val pw = at(inforef, readSymbol) - inforef = readNat() - pw - } - // println("reading type for " + denot) // !!! DEBUG - val tp = at(inforef, readType) - denot match { - case denot: ClassDenotation => - val selfInfo = if (atEnd) NoType else readTypeRef() - setClassInfo(denot, tp, selfInfo) - case denot => - val tp1 = translateTempPoly(tp) - denot.info = - if (tag == ALIASsym) TypeAlias(tp1) - else if (denot.isType) checkNonCyclic(denot.symbol, tp1, reportErrors = false) - // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles - else if (!denot.is(Param)) tp1.underlyingIfRepeated(isJava = false) - else tp1 - if (denot.isConstructor) addConstructorTypeParams(denot) - if (atEnd) { - assert(!(denot is SuperAccessor), denot) - } else { - assert(denot is (SuperAccessor | ParamAccessor), denot) - def disambiguate(alt: Symbol) = { // !!! DEBUG - ctx.debugTraceIndented(s"disambiguating ${denot.info} =:= ${denot.owner.thisType.memberInfo(alt)} ${denot.owner}") { - denot.info matches denot.owner.thisType.memberInfo(alt) - } - } - val alias = readDisambiguatedSymbolRef(disambiguate).asTerm - denot.addAnnotation(Annotation.makeAlias(alias)) - } - } - // println(s"unpickled ${denot.debugString}, info = ${denot.info}") !!! DEBUG - } - atReadPos(startCoord(denot).toIndex, - () => parseToCompletion(denot)( - ctx.addMode(Mode.Scala2Unpickling).withPhaseNoLater(ctx.picklerPhase))) - } catch { - case ex: RuntimeException => handleRuntimeException(ex) - } - } - - object localMemberUnpickler extends LocalUnpickler - - class ClassUnpickler(infoRef: Int) extends LocalUnpickler with TypeParamsCompleter { - private def readTypeParams()(implicit ctx: Context): List[TypeSymbol] = { - val tag = readByte() - val end = readNat() + readIndex - if (tag == POLYtpe) { - val unusedRestpeRef = readNat() - until(end, readSymbolRef).asInstanceOf[List[TypeSymbol]] - } else Nil - } - private def loadTypeParams(implicit ctx: Context) = - atReadPos(index(infoRef), readTypeParams) - - /** Force reading type params early, we need them in setClassInfo of subclasses. */ - def init()(implicit ctx: Context) = loadTypeParams - - def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = - loadTypeParams - } - - def rootClassUnpickler(start: Coord, cls: Symbol, module: Symbol, infoRef: Int) = - (new ClassUnpickler(infoRef) with SymbolLoaders.SecondCompleter { - override def startCoord(denot: SymDenotation): Coord = start - }) withDecls symScope(cls) withSourceModule (_ => module) - - /** Convert - * tp { type name = sym } forSome { sym >: L <: H } - * to - * tp { name >: L <: H } - * and - * tp { name: sym } forSome { sym <: T with Singleton } - * to - * tp { name: T } - */ - def elimExistentials(boundSyms: List[Symbol], tp: Type)(implicit ctx: Context): Type = { - // Need to be careful not to run into cyclic references here (observed when - // comiling t247.scala). That's why we avoiud taking `symbol` of a TypeRef - // unless names match up. - val isBound = (tp: Type) => { - def refersTo(tp: Type, sym: Symbol): Boolean = tp match { - case tp @ TypeRef(_, name) => sym.name == name && sym == tp.symbol - case tp: TypeVar => refersTo(tp.underlying, sym) - case tp : LazyRef => refersTo(tp.ref, sym) - case _ => false - } - boundSyms.exists(refersTo(tp, _)) - } - // Cannot use standard `existsPart` method because it calls `lookupRefined` - // which can cause CyclicReference errors. - val isBoundAccumulator = new ExistsAccumulator(isBound) { - override def foldOver(x: Boolean, tp: Type): Boolean = tp match { - case tp: TypeRef => applyToPrefix(x, tp) - case _ => super.foldOver(x, tp) - } - } - def removeSingleton(tp: Type): Type = - if (tp isRef defn.SingletonClass) defn.AnyType else tp - def elim(tp: Type): Type = tp match { - case tp @ RefinedType(parent, name, rinfo) => - val parent1 = elim(tp.parent) - rinfo match { - case TypeAlias(info: TypeRef) if isBound(info) => - RefinedType(parent1, name, info.symbol.info) - case info: TypeRef if isBound(info) => - val info1 = info.symbol.info - assert(info1.derivesFrom(defn.SingletonClass)) - RefinedType(parent1, name, info1.mapReduceAnd(removeSingleton)(_ & _)) - case info => - tp.derivedRefinedType(parent1, name, info) - } - case tp @ HKApply(tycon, args) => - val tycon1 = tycon.safeDealias - def mapArg(arg: Type) = arg match { - case arg: TypeRef if isBound(arg) => arg.symbol.info - case _ => arg - } - if (tycon1 ne tycon) elim(tycon1.appliedTo(args)) - else tp.derivedAppliedType(tycon, args.map(mapArg)) - case _ => - tp - } - val tp1 = elim(tp) - if (isBoundAccumulator(false, tp1)) { - val anyTypes = boundSyms map (_ => defn.AnyType) - val boundBounds = boundSyms map (_.info.bounds.hi) - val tp2 = tp1.subst(boundSyms, boundBounds).subst(boundSyms, anyTypes) - ctx.warning(s"""failure to eliminate existential - |original type : $tp forSome {${ctx.dclsText(boundSyms, "; ").show} - |reduces to : $tp1 - |type used instead: $tp2 - |proceed at own risk.""".stripMargin) - tp2 - } else tp1 - } - - /** Read a type - * - * @param forceProperType is used to ease the transition to NullaryMethodTypes (commentmarker: NMT_TRANSITION) - * the flag say that a type of kind * is expected, so that PolyType(tps, restpe) can be disambiguated to PolyType(tps, NullaryMethodType(restpe)) - * (if restpe is not a ClassInfoType, a MethodType or a NullaryMethodType, which leaves TypeRef/SingletonType -- the latter would make the polytype a type constructor) - */ - protected def readType()(implicit ctx: Context): Type = { - val tag = readByte() - val end = readNat() + readIndex - (tag: @switch) match { - case NOtpe => - NoType - case NOPREFIXtpe => - NoPrefix - case THIStpe => - readSymbolRef().thisType - case SINGLEtpe => - val pre = readTypeRef() - val sym = readDisambiguatedSymbolRef(_.info.isParameterless) - if (isLocal(sym) || (pre == NoPrefix)) pre select sym - else TermRef.withSig(pre, sym.name.asTermName, Signature.NotAMethod) // !!! should become redundant - case SUPERtpe => - val thistpe = readTypeRef() - val supertpe = readTypeRef() - SuperType(thistpe, supertpe) - case CONSTANTtpe => - ConstantType(readConstantRef()) - case TYPEREFtpe => - var pre = readTypeRef() - val sym = readSymbolRef() - pre match { - case thispre: ThisType => - // The problem is that class references super.C get pickled as - // this.C. Dereferencing the member might then get an overriding class - // instance. The problem arises for instance for LinkedHashMap#MapValues - // and also for the inner Transform class in all views. We fix it by - // replacing the this with the appropriate super. - if (sym.owner != thispre.cls) { - val overriding = thispre.cls.info.decls.lookup(sym.name) - if (overriding.exists && overriding != sym) { - val base = pre.baseTypeWithArgs(sym.owner) - assert(base.exists) - pre = SuperType(thispre, base) - } - } - case _ => - } - val tycon = - if (sym.isClass && sym.is(Scala2x) && !sym.owner.is(Package)) - // used fixed sym for Scala 2 inner classes, because they might be shadowed - TypeRef.withFixedSym(pre, sym.name.asTypeName, sym.asType) - else if (isLocal(sym) || pre == NoPrefix) { - val pre1 = if ((pre eq NoPrefix) && (sym is TypeParam)) sym.owner.thisType else pre - pre1 select sym - } - else TypeRef(pre, sym.name.asTypeName) - val args = until(end, readTypeRef) - if (sym == defn.ByNameParamClass2x) ExprType(args.head) - else if (args.nonEmpty) tycon.safeAppliedTo(EtaExpandIfHK(sym.typeParams, args)) - else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams) - else tycon - case TYPEBOUNDStpe => - TypeBounds(readTypeRef(), readTypeRef()) - case REFINEDtpe => - val clazz = readSymbolRef() - val decls = symScope(clazz) - symScopes(clazz) = EmptyScope // prevent further additions - val parents = until(end, readTypeRef) - val parent = parents.reduceLeft(AndType(_, _)) - if (decls.isEmpty) parent - else { - def subst(info: Type, rt: RecType) = - if (clazz.isClass) info.substThis(clazz.asClass, RecThis(rt)) - else info // turns out some symbols read into `clazz` are not classes, not sure why this is the case. - def addRefinement(tp: Type, sym: Symbol) = RefinedType(tp, sym.name, sym.info) - val refined = (parent /: decls.toList)(addRefinement) - RecType.closeOver(rt => subst(refined, rt)) - } - case CLASSINFOtpe => - val clazz = readSymbolRef() - TempClassInfoType(until(end, readTypeRef), symScope(clazz), clazz) - case METHODtpe | IMPLICITMETHODtpe => - val restpe = readTypeRef() - val params = until(end, readSymbolRef) - def isImplicit = - tag == IMPLICITMETHODtpe || - params.nonEmpty && (params.head is Implicit) - val maker = if (isImplicit) ImplicitMethodType else MethodType - maker.fromSymbols(params, restpe) - case POLYtpe => - val restpe = readTypeRef() - val typeParams = until(end, readSymbolRef) - if (typeParams.nonEmpty) TempPolyType(typeParams.asInstanceOf[List[TypeSymbol]], restpe.widenExpr) - else ExprType(restpe) - case EXISTENTIALtpe => - val restpe = readTypeRef() - val boundSyms = until(end, readSymbolRef) - elimExistentials(boundSyms, restpe) - case ANNOTATEDtpe => - AnnotatedType.make(readTypeRef(), until(end, readAnnotationRef)) - case _ => - noSuchTypeTag(tag, end) - } - } - - def readTypeParams()(implicit ctx: Context): List[Symbol] = { - val tag = readByte() - val end = readNat() + readIndex - if (tag == POLYtpe) { - val unusedRestperef = readNat() - until(end, readSymbolRef) - } else Nil - } - - def noSuchTypeTag(tag: Int, end: Int)(implicit ctx: Context): Type = - errorBadSignature("bad type tag: " + tag) - - /** Read a constant */ - protected def readConstant()(implicit ctx: Context): Constant = { - val tag = readByte().toInt - val len = readNat() - (tag: @switch) match { - case LITERALunit => Constant(()) - case LITERALboolean => Constant(readLong(len) != 0L) - case LITERALbyte => Constant(readLong(len).toByte) - case LITERALshort => Constant(readLong(len).toShort) - case LITERALchar => Constant(readLong(len).toChar) - case LITERALint => Constant(readLong(len).toInt) - case LITERALlong => Constant(readLong(len)) - case LITERALfloat => Constant(intBitsToFloat(readLong(len).toInt)) - case LITERALdouble => Constant(longBitsToDouble(readLong(len))) - case LITERALstring => Constant(readNameRef().toString) - case LITERALnull => Constant(null) - case LITERALclass => Constant(readTypeRef()) - case LITERALenum => Constant(readSymbolRef()) - case _ => noSuchConstantTag(tag, len) - } - } - - def noSuchConstantTag(tag: Int, len: Int)(implicit ctx: Context): Constant = - errorBadSignature("bad constant tag: " + tag) - - /** Read children and store them into the corresponding symbol. - */ - protected def readChildren()(implicit ctx: Context): Unit = { - val tag = readByte() - assert(tag == CHILDREN) - val end = readNat() + readIndex - val target = readSymbolRef() - while (readIndex != end) - target.addAnnotation(Annotation.makeChild(readSymbolRef())) - } - - /* Read a reference to a pickled item */ - protected def readSymbolRef()(implicit ctx: Context): Symbol = { //OPT inlined from: at(readNat(), readSymbol) to save on closure creation - val i = readNat() - var r = entries(i) - if (r eq null) { - val savedIndex = readIndex - readIndex = index(i) - r = readSymbol() - assert(entries(i) eq null, entries(i)) - entries(i) = r - readIndex = savedIndex - } - r.asInstanceOf[Symbol] - } - - protected def readDisambiguatedSymbolRef(p: Symbol => Boolean)(implicit ctx: Context): Symbol = - at(readNat(), readDisambiguatedSymbol(p)) - - protected def readNameRef()(implicit ctx: Context): Name = at(readNat(), readName) - protected def readTypeRef()(implicit ctx: Context): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... () - protected def readConstantRef()(implicit ctx: Context): Constant = at(readNat(), readConstant) - - protected def readTypeNameRef()(implicit ctx: Context): TypeName = readNameRef().toTypeName - protected def readTermNameRef()(implicit ctx: Context): TermName = readNameRef().toTermName - - protected def readAnnotationRef()(implicit ctx: Context): Annotation = at(readNat(), readAnnotation) - - protected def readModifiersRef(isType: Boolean)(implicit ctx: Context): Modifiers = at(readNat(), () => readModifiers(isType)) - protected def readTreeRef()(implicit ctx: Context): Tree = at(readNat(), readTree) - - /** Read an annotation argument, which is pickled either - * as a Constant or a Tree. - */ - protected def readAnnotArg(i: Int)(implicit ctx: Context): Tree = bytes(index(i)) match { - case TREE => at(i, readTree) - case _ => Literal(at(i, readConstant)) - } - - /** Read a ClassfileAnnotArg (argument to a classfile annotation) - */ - private def readArrayAnnotArg()(implicit ctx: Context): Tree = { - readByte() // skip the `annotargarray` tag - val end = readNat() + readIndex - // array elements are trees representing instances of scala.annotation.Annotation - SeqLiteral( - until(end, () => readClassfileAnnotArg(readNat())), - TypeTree(defn.AnnotationType)) - } - - private def readAnnotInfoArg()(implicit ctx: Context): Tree = { - readByte() // skip the `annotinfo` tag - val end = readNat() + readIndex - readAnnotationContents(end) - } - - protected def readClassfileAnnotArg(i: Int)(implicit ctx: Context): Tree = bytes(index(i)) match { - case ANNOTINFO => at(i, readAnnotInfoArg) - case ANNOTARGARRAY => at(i, readArrayAnnotArg) - case _ => readAnnotArg(i) - } - - /** Read an annotation's contents. Not to be called directly, use - * readAnnotation, readSymbolAnnotation, or readAnnotInfoArg - */ - protected def readAnnotationContents(end: Int)(implicit ctx: Context): Tree = { - val atp = readTypeRef() - val args = { - val t = new ListBuffer[Tree] - - while (readIndex != end) { - val argref = readNat() - t += { - if (isNameEntry(argref)) { - val name = at(argref, readName) - val arg = readClassfileAnnotArg(readNat()) - NamedArg(name.asTermName, arg) - } else readAnnotArg(argref) - } - } - t.toList - } - // println(atp) - val targs = atp.argTypes - - tpd.applyOverloaded(tpd.New(atp withoutArgs targs), nme.CONSTRUCTOR, args, targs, atp) -} - - /** Read an annotation and as a side effect store it into - * the symbol it requests. Called at top-level, for all - * (symbol, annotInfo) entries. - */ - protected def readSymbolAnnotation()(implicit ctx: Context): Unit = { - val tag = readByte() - if (tag != SYMANNOT) - errorBadSignature("symbol annotation expected (" + tag + ")") - val end = readNat() + readIndex - val target = readSymbolRef() - target.addAnnotation(deferredAnnot(end)) - } - - /** Read an annotation and return it. Used when unpickling - * an ANNOTATED(WSELF)tpe or a NestedAnnotArg - */ - protected def readAnnotation()(implicit ctx: Context): Annotation = { - val tag = readByte() - if (tag != ANNOTINFO) - errorBadSignature("annotation expected (" + tag + ")") - val end = readNat() + readIndex - deferredAnnot(end) - } - - /** A deferred annotation that can be completed by reading - * the bytes between `readIndex` and `end`. - */ - protected def deferredAnnot(end: Int)(implicit ctx: Context): Annotation = { - val start = readIndex - val atp = readTypeRef() - Annotation.deferred( - atp.typeSymbol, implicit ctx1 => - atReadPos(start, () => readAnnotationContents(end)(ctx1.withPhase(ctx.phase)))) - } - - /* Read an abstract syntax tree */ - protected def readTree()(implicit ctx: Context): Tree = { - val outerTag = readByte() - if (outerTag != TREE) - errorBadSignature("tree expected (" + outerTag + ")") - val end = readNat() + readIndex - val tag = readByte() - val tpe = if (tag == EMPTYtree) NoType else readTypeRef() - - // Set by the three functions to follow. If symbol is non-null - // after the new tree 't' has been created, t has its Symbol - // set to symbol; and it always has its Type set to tpe. - var symbol: Symbol = null - var mods: Modifiers = null - var name: Name = null - - /** Read a Symbol, Modifiers, and a Name */ - def setSymModsName(): Unit = { - symbol = readSymbolRef() - mods = readModifiersRef(symbol.isType) - name = readNameRef() - } - /** Read a Symbol and a Name */ - def setSymName(): Unit = { - symbol = readSymbolRef() - name = readNameRef() - } - /** Read a Symbol */ - def setSym(): Unit = { - symbol = readSymbolRef() - } - - implicit val pos: Position = NoPosition - - tag match { - case EMPTYtree => - EmptyTree - - case PACKAGEtree => - setSym() - val pid = readTreeRef().asInstanceOf[RefTree] - val stats = until(end, readTreeRef) - PackageDef(pid, stats) - - case CLASStree => - setSymModsName() - val impl = readTemplateRef() - val tparams = until(end, readTypeDefRef) - val cls = symbol.asClass - val ((constr: DefDef) :: Nil, stats) = - impl.body.partition(_.symbol == cls.primaryConstructor) - ClassDef(cls, constr, tparams ++ stats) - - case MODULEtree => - setSymModsName() - ModuleDef(symbol.asTerm, readTemplateRef().body) - - case VALDEFtree => - setSymModsName() - val tpt = readTreeRef() - val rhs = readTreeRef() - ValDef(symbol.asTerm, rhs) - - case DEFDEFtree => - setSymModsName() - val tparams = times(readNat(), readTypeDefRef) - val vparamss = times(readNat(), () => times(readNat(), readValDefRef)) - val tpt = readTreeRef() - val rhs = readTreeRef() - DefDef(symbol.asTerm, rhs) - - case TYPEDEFtree => - setSymModsName() - val rhs = readTreeRef() - val tparams = until(end, readTypeDefRef) - TypeDef(symbol.asType) - - case LABELtree => - setSymName() - val rhs = readTreeRef() - val params = until(end, readIdentRef) - val ldef = DefDef(symbol.asTerm, rhs) - def isCaseLabel(sym: Symbol) = sym.name.startsWith(nme.CASEkw) - if (isCaseLabel(symbol)) ldef - else Block(ldef :: Nil, Apply(Ident(symbol.termRef), Nil)) - - case IMPORTtree => - setSym() - val expr = readTreeRef() - val selectors = until(end, () => { - val fromName = readNameRef() - val toName = readNameRef() - val from = untpd.Ident(fromName) - val to = untpd.Ident(toName) - if (toName.isEmpty) from else untpd.Thicket(from, untpd.Ident(toName)) - }) - - Import(expr, selectors) - - case TEMPLATEtree => - setSym() - val parents = times(readNat(), readTreeRef) - val self = readValDefRef() - val body = until(end, readTreeRef) - untpd.Template(???, parents, self, body) // !!! TODO: pull out primary constructor - .withType(symbol.namedType) - - case BLOCKtree => - val expr = readTreeRef() - val stats = until(end, readTreeRef) - Block(stats, expr) - - case CASEtree => - val pat = readTreeRef() - val guard = readTreeRef() - val body = readTreeRef() - CaseDef(pat, guard, body) - - case ALTERNATIVEtree => - Alternative(until(end, readTreeRef)) - - case STARtree => - readTreeRef() - unimplementedTree("STAR") - - case BINDtree => - setSymName() - Bind(symbol.asTerm, readTreeRef()) - - case UNAPPLYtree => - val fun = readTreeRef() - val args = until(end, readTreeRef) - UnApply(fun, Nil, args, defn.AnyType) // !!! this is wrong in general - - case ARRAYVALUEtree => - val elemtpt = readTreeRef() - val trees = until(end, readTreeRef) - SeqLiteral(trees, elemtpt) - // note can't deal with trees passed to Java methods as arrays here - - case FUNCTIONtree => - setSym() - val body = readTreeRef() - val vparams = until(end, readValDefRef) - val applyType = MethodType(vparams map (_.name), vparams map (_.tpt.tpe), body.tpe) - val applyMeth = ctx.newSymbol(symbol.owner, nme.apply, Method, applyType) - Closure(applyMeth, Function.const(body.changeOwner(symbol, applyMeth)) _) - - case ASSIGNtree => - val lhs = readTreeRef() - val rhs = readTreeRef() - Assign(lhs, rhs) - - case IFtree => - val cond = readTreeRef() - val thenp = readTreeRef() - val elsep = readTreeRef() - If(cond, thenp, elsep) - - case MATCHtree => - val selector = readTreeRef() - val cases = until(end, readCaseDefRef) - Match(selector, cases) - - case RETURNtree => - setSym() - Return(readTreeRef(), Ident(symbol.termRef)) - - case TREtree => - val block = readTreeRef() - val finalizer = readTreeRef() - val catches = until(end, readCaseDefRef) - Try(block, catches, finalizer) - - case THROWtree => - Throw(readTreeRef()) - - case NEWtree => - New(readTreeRef().tpe) - - case TYPEDtree => - val expr = readTreeRef() - val tpt = readTreeRef() - Typed(expr, tpt) - - case TYPEAPPLYtree => - val fun = readTreeRef() - val args = until(end, readTreeRef) - TypeApply(fun, args) - - case APPLYtree => - val fun = readTreeRef() - val args = until(end, readTreeRef) - /* - if (fun.symbol.isOverloaded) { - fun.setType(fun.symbol.info) - inferMethodAlternative(fun, args map (_.tpe), tpe) - } -*/ - Apply(fun, args) // note: can't deal with overloaded syms yet - - case APPLYDYNAMICtree => - setSym() - val qual = readTreeRef() - val args = until(end, readTreeRef) - unimplementedTree("APPLYDYNAMIC") - - case SUPERtree => - setSym() - val qual = readTreeRef() - val mix = readTypeNameRef() - Super(qual, mix, inConstrCall = false) // todo: revise - - case THIStree => - setSym() - val name = readTypeNameRef() - This(symbol.asClass) - - case SELECTtree => - setSym() - val qualifier = readTreeRef() - val selector = readNameRef() - qualifier.select(symbol.namedType) - case IDENTtree => - setSymName() - Ident(symbol.namedType) - - case LITERALtree => - Literal(readConstantRef()) - - case TYPEtree => - TypeTree(tpe) - - case ANNOTATEDtree => - val annot = readTreeRef() - val arg = readTreeRef() - Annotated(arg, annot) - - case SINGLETONTYPEtree => - SingletonTypeTree(readTreeRef()) - - case SELECTFROMTYPEtree => - val qualifier = readTreeRef() - val selector = readTypeNameRef() - Select(qualifier, symbol.namedType) - - case COMPOUNDTYPEtree => - readTemplateRef() - TypeTree(tpe) - - case APPLIEDTYPEtree => - val tpt = readTreeRef() - val args = until(end, readTreeRef) - AppliedTypeTree(tpt, args) - - case TYPEBOUNDStree => - val lo = readTreeRef() - val hi = readTreeRef() - TypeBoundsTree(lo, hi) - - case EXISTENTIALTYPEtree => - val tpt = readTreeRef() - val whereClauses = until(end, readTreeRef) - TypeTree(tpe) - - case _ => - noSuchTreeTag(tag, end) - } - } - - def noSuchTreeTag(tag: Int, end: Int)(implicit ctx: Context) = - errorBadSignature("unknown tree type (" + tag + ")") - - def unimplementedTree(what: String)(implicit ctx: Context) = - errorBadSignature(s"cannot read $what trees from Scala 2.x signatures") - - def readModifiers(isType: Boolean)(implicit ctx: Context): Modifiers = { - val tag = readNat() - if (tag != MODIFIERS) - errorBadSignature("expected a modifiers tag (" + tag + ")") - val end = readNat() + readIndex - val pflagsHi = readNat() - val pflagsLo = readNat() - val pflags = (pflagsHi.toLong << 32) + pflagsLo - val flags = unpickleScalaFlags(pflags, isType) - val privateWithin = readNameRef().asTypeName - Modifiers(flags, privateWithin, Nil) - } - - protected def readTemplateRef()(implicit ctx: Context): Template = - readTreeRef() match { - case templ: Template => templ - case other => - errorBadSignature("expected a template (" + other + ")") - } - protected def readCaseDefRef()(implicit ctx: Context): CaseDef = - readTreeRef() match { - case tree: CaseDef => tree - case other => - errorBadSignature("expected a case def (" + other + ")") - } - protected def readValDefRef()(implicit ctx: Context): ValDef = - readTreeRef() match { - case tree: ValDef => tree - case other => - errorBadSignature("expected a ValDef (" + other + ")") - } - protected def readIdentRef()(implicit ctx: Context): Ident = - readTreeRef() match { - case tree: Ident => tree - case other => - errorBadSignature("expected an Ident (" + other + ")") - } - protected def readTypeDefRef()(implicit ctx: Context): TypeDef = - readTreeRef() match { - case tree: TypeDef => tree - case other => - errorBadSignature("expected an TypeDef (" + other + ")") - } - -} diff --git a/src/dotty/tools/dotc/parsing/CharArrayReader.scala b/src/dotty/tools/dotc/parsing/CharArrayReader.scala deleted file mode 100644 index b84e2eb47..000000000 --- a/src/dotty/tools/dotc/parsing/CharArrayReader.scala +++ /dev/null @@ -1,132 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import scala.reflect.internal.Chars._ - -abstract class CharArrayReader { self => - - val buf: Array[Char] - protected def startFrom = 0 - - /** Switch whether unicode should be decoded */ - protected def decodeUni: Boolean = true - - /** An error routine to call on bad unicode escapes \\uxxxx. */ - protected def error(msg: String, offset: Int): Unit - - /** the last read character */ - var ch: Char = _ - - /** The offset one past the last read character */ - var charOffset: Int = startFrom - - /** The offset before the last read character */ - var lastCharOffset: Int = startFrom - - /** The start offset of the current line */ - var lineStartOffset: Int = startFrom - - /** The start offset of the line before the current one */ - var lastLineStartOffset: Int = startFrom - - private var lastUnicodeOffset = -1 - - /** Is last character a unicode escape \\uxxxx? */ - def isUnicodeEscape = charOffset == lastUnicodeOffset - - /** Advance one character; reducing CR;LF pairs to just LF */ - final def nextChar(): Unit = { - val idx = charOffset - lastCharOffset = idx - if (idx >= buf.length) { - ch = SU - } else { - val c = buf(idx) - ch = c - charOffset = idx + 1 - if (c == '\\') potentialUnicode() - else if (c < ' ') { skipCR(); potentialLineEnd() } - } - } - - def getc() = { nextChar() ; ch } - - /** Advance one character, leaving CR;LF pairs intact. - * This is for use in multi-line strings, so there are no - * "potential line ends" here. - */ - final def nextRawChar(): Unit = { - val idx = charOffset - lastCharOffset = idx - if (idx >= buf.length) { - ch = SU - } else { - val c = buf(charOffset) - ch = c - charOffset = idx + 1 - if (c == '\\') potentialUnicode() - } - } - - /** Interpret \\uxxxx escapes */ - private def potentialUnicode(): Unit = { - def evenSlashPrefix: Boolean = { - var p = charOffset - 2 - while (p >= 0 && buf(p) == '\\') p -= 1 - (charOffset - p) % 2 == 0 - } - def udigit: Int = { - if (charOffset >= buf.length) { - // Since the positioning code is very insistent about throwing exceptions, - // we have to decrement the position so our error message can be seen, since - // we are one past EOF. This happens with e.g. val x = \ u 1 <EOF> - error("incomplete unicode escape", charOffset - 1) - SU - } - else { - val d = digit2int(buf(charOffset), 16) - if (d >= 0) charOffset += 1 - else error("error in unicode escape", charOffset) - d - } - } - if (charOffset < buf.length && buf(charOffset) == 'u' && decodeUni && evenSlashPrefix) { - do charOffset += 1 - while (charOffset < buf.length && buf(charOffset) == 'u') - val code = udigit << 12 | udigit << 8 | udigit << 4 | udigit - lastUnicodeOffset = charOffset - ch = code.toChar - } - } - - /** replace CR;LF by LF */ - private def skipCR(): Unit = { - if (ch == CR) - if (charOffset < buf.length && buf(charOffset) == LF) { - charOffset += 1 - ch = LF - } - } - - /** Handle line ends */ - private def potentialLineEnd(): Unit = { - if (ch == LF || ch == FF) { - lastLineStartOffset = lineStartOffset - lineStartOffset = charOffset - } - } - - def isAtEnd = charOffset >= buf.length - - /** A new reader that takes off at the current character position */ - def lookaheadReader = new CharArrayLookaheadReader - - class CharArrayLookaheadReader extends CharArrayReader { - val buf = self.buf - charOffset = self.charOffset - ch = self.ch - override def decodeUni = self.decodeUni - def error(msg: String, offset: Int) = self.error(msg, offset) - } -} diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala deleted file mode 100644 index 0f63b25bb..000000000 --- a/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ /dev/null @@ -1,898 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.core.Flags -import dotty.tools.dotc.core.Flags.FlagSet - -import scala.language.implicitConversions - -import JavaTokens._ -import JavaScanners._ -import Scanners.Offset -import Parsers._ -import core._ -import Contexts._ -import Names._ -import NameOps._ -import Types._ -import Symbols._ -import ast.Trees._ -import Decorators._ -import StdNames._ -import dotty.tools.dotc.reporting.diagnostic.messages.IdentifierExpected -import dotty.tools.dotc.util.SourceFile -import util.Positions._ -import annotation.switch -import scala.collection.mutable.ListBuffer -import scala.reflect.internal.util.Collections._ - -object JavaParsers { - - import ast.untpd._ - - class JavaParser(source: SourceFile)(implicit ctx: Context) extends ParserCommon(source) { - - val definitions = ctx.definitions - import definitions._ - - val in: JavaScanner = new JavaScanner(source) - - /** The simple name of the package of the currently parsed file */ - private var thisPackageName: TypeName = tpnme.EMPTY - - /** This is the general parse entry point. - * Overridden by ScriptParser - */ - def parse(): Tree = { - val t = compilationUnit() - accept(EOF) - t - } - - // -------- error handling --------------------------------------- - - protected def skip(): Unit = { - var nparens = 0 - var nbraces = 0 - while (true) { - in.token match { - case EOF => - return - case SEMI => - if (nparens == 0 && nbraces == 0) return - case RPAREN => - nparens -= 1 - case RBRACE => - if (nbraces == 0) return - nbraces -= 1 - case LPAREN => - nparens += 1 - case LBRACE => - nbraces += 1 - case _ => - } - in.nextToken() - } - } - - def syntaxError(msg: String, skipIt: Boolean): Unit = { - syntaxError(in.offset, msg, skipIt) - } - - def syntaxError(pos: Int, msg: String, skipIt: Boolean): Unit = { - if (pos > lastErrorOffset) { - syntaxError(msg, pos) - // no more errors on this token. - lastErrorOffset = in.offset - } - if (skipIt) - skip() - } - def errorTypeTree = TypeTree().withType(ErrorType) withPos Position(in.offset) - - // --------- tree building ----------------------------- - - def scalaAnnotationDot(name: Name) = Select(scalaDot(nme.annotation), name) - - def javaDot(name: Name): Tree = - Select(rootDot(nme.java), name) - - def javaLangDot(name: Name): Tree = - Select(javaDot(nme.lang), name) - - def javaLangObject(): Tree = javaLangDot(tpnme.Object) - - def arrayOf(tpt: Tree) = - AppliedTypeTree(Ident(nme.Array.toTypeName), List(tpt)) - - def unimplementedExpr = Ident("???".toTermName) - - def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef], needsDummyConstr: Boolean) = { - def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match { - case (meth: DefDef) :: rest if meth.name == CONSTRUCTOR => (meth, rest) - case first :: rest => - val (constr, tail) = pullOutFirstConstr(rest) - (constr, first :: tail) - case nil => (EmptyTree, nil) - } - var (constr1, stats1) = pullOutFirstConstr(stats) - if (constr1 == EmptyTree) constr1 = makeConstructor(List(), tparams) - // A dummy first constructor is needed for Java classes so that the real constructors see the - // import of the companion object. The constructor has parameter of type Unit so no Java code - // can call it. - if (needsDummyConstr) { - stats1 = constr1 :: stats1 - constr1 = makeConstructor(List(scalaDot(tpnme.Unit)), tparams, Flags.JavaDefined | Flags.PrivateLocal) - } - Template(constr1.asInstanceOf[DefDef], parents, EmptyValDef, stats1) - } - - def makeSyntheticParam(count: Int, tpt: Tree): ValDef = - makeParam(nme.syntheticParamName(count), tpt) - def makeParam(name: TermName, tpt: Tree): ValDef = - ValDef(name, tpt, EmptyTree).withMods(Modifiers(Flags.JavaDefined | Flags.ParamAccessor)) - - def makeConstructor(formals: List[Tree], tparams: List[TypeDef], flags: FlagSet = Flags.JavaDefined) = { - val vparams = mapWithIndex(formals)((p, i) => makeSyntheticParam(i + 1, p)) - DefDef(nme.CONSTRUCTOR, tparams, List(vparams), TypeTree(), EmptyTree).withMods(Modifiers(flags)) - } - - // ------------- general parsing --------------------------- - - /** skip parent or brace enclosed sequence of things */ - def skipAhead(): Unit = { - var nparens = 0 - var nbraces = 0 - do { - in.token match { - case LPAREN => - nparens += 1 - case LBRACE => - nbraces += 1 - case _ => - } - in.nextToken() - in.token match { - case RPAREN => - nparens -= 1 - case RBRACE => - nbraces -= 1 - case _ => - } - } while (in.token != EOF && (nparens > 0 || nbraces > 0)) - } - - def skipTo(tokens: Int*): Unit = { - while (!(tokens contains in.token) && in.token != EOF) { - if (in.token == LBRACE) { skipAhead(); accept(RBRACE) } - else if (in.token == LPAREN) { skipAhead(); accept(RPAREN) } - else in.nextToken() - } - } - - /** Consume one token of the specified type, or - * signal an error if it is not there. - * - * @return The offset at the start of the token to accept - */ - def accept(token: Int): Int = { - val offset = in.offset - if (in.token != token) { - val offsetToReport = in.offset - val msg = - tokenString(token) + " expected but " + - tokenString(in.token) + " found." - - syntaxError(offsetToReport, msg, skipIt = true) - } - if (in.token == token) in.nextToken() - offset - } - - def acceptClosingAngle(): Unit = { - val closers: PartialFunction[Int, Int] = { - case GTGTGTEQ => GTGTEQ - case GTGTGT => GTGT - case GTGTEQ => GTEQ - case GTGT => GT - case GTEQ => EQUALS - } - if (closers isDefinedAt in.token) in.token = closers(in.token) - else accept(GT) - } - - def identForType(): TypeName = ident().toTypeName - def ident(): Name = - if (in.token == IDENTIFIER) { - val name = in.name - in.nextToken() - name - } else { - accept(IDENTIFIER) - nme.ERROR - } - - def repsep[T <: Tree](p: () => T, sep: Int): List[T] = { - val buf = ListBuffer[T](p()) - while (in.token == sep) { - in.nextToken() - buf += p() - } - buf.toList - } - - /** Convert (qual)ident to type identifier - */ - def convertToTypeId(tree: Tree): Tree = convertToTypeName(tree) match { - case Some(t) => t withPos tree.pos - case _ => tree match { - case AppliedTypeTree(_, _) | Select(_, _) => - tree - case _ => - syntaxError(IdentifierExpected(tree.show), tree.pos) - errorTypeTree - } - } - - /** Translate names in Select/Ident nodes to type names. - */ - def convertToTypeName(tree: Tree): Option[RefTree] = tree match { - case Select(qual, name) => Some(Select(qual, name.toTypeName)) - case Ident(name) => Some(Ident(name.toTypeName)) - case _ => None - } - // -------------------- specific parsing routines ------------------ - - def qualId(): RefTree = { - var t: RefTree = atPos(in.offset) { Ident(ident()) } - while (in.token == DOT) { - in.nextToken() - t = atPos(t.pos.start, in.offset) { Select(t, ident()) } - } - t - } - - def optArrayBrackets(tpt: Tree): Tree = - if (in.token == LBRACKET) { - val tpt1 = atPos(tpt.pos.start, in.offset) { arrayOf(tpt) } - in.nextToken() - accept(RBRACKET) - optArrayBrackets(tpt1) - } else tpt - - def basicType(): Tree = - atPos(in.offset) { - in.token match { - case BYTE => in.nextToken(); TypeTree(ByteType) - case SHORT => in.nextToken(); TypeTree(ShortType) - case CHAR => in.nextToken(); TypeTree(CharType) - case INT => in.nextToken(); TypeTree(IntType) - case LONG => in.nextToken(); TypeTree(LongType) - case FLOAT => in.nextToken(); TypeTree(FloatType) - case DOUBLE => in.nextToken(); TypeTree(DoubleType) - case BOOLEAN => in.nextToken(); TypeTree(BooleanType) - case _ => syntaxError("illegal start of type", skipIt = true); errorTypeTree - } - } - - def typ(): Tree = - optArrayBrackets { - if (in.token == FINAL) in.nextToken() - if (in.token == IDENTIFIER) { - var t = typeArgs(atPos(in.offset)(Ident(ident()))) - // typeSelect generates Select nodes if the lhs is an Ident or Select, - // For other nodes it always assumes that the selected item is a type. - def typeSelect(t: Tree, name: Name) = t match { - case Ident(_) | Select(_, _) => Select(t, name) - case _ => Select(t, name.toTypeName) - } - while (in.token == DOT) { - in.nextToken() - t = typeArgs(atPos(t.pos.start, in.offset)(typeSelect(t, ident()))) - } - convertToTypeId(t) - } else { - basicType() - } - } - - def typeArgs(t: Tree): Tree = { - var wildnum = 0 - def typeArg(): Tree = - if (in.token == QMARK) { - val offset = in.offset - in.nextToken() - val hi = if (in.token == EXTENDS) { in.nextToken() ; typ() } else EmptyTree - val lo = if (in.token == SUPER) { in.nextToken() ; typ() } else EmptyTree - atPos(offset) { - /* - TypeDef( - Modifiers(Flags.JavaDefined | Flags.Deferred), - typeName("_$" +(wildnum += 1)), - List(), - TypeBoundsTree(lo, hi)) - */ - TypeBoundsTree(lo, hi) - } - } else { - typ() - } - if (in.token == LT) { - in.nextToken() - val t1 = convertToTypeId(t) - val args = repsep(typeArg, COMMA) - acceptClosingAngle() - atPos(t1.pos.start) { - AppliedTypeTree(t1, args) - } - } else t - } - - def annotations(): List[Tree] = { - //var annots = new ListBuffer[Tree] - while (in.token == AT) { - in.nextToken() - annotation() - } - List() // don't pass on annotations for now - } - - /** Annotation ::= TypeName [`(` AnnotationArgument {`,` AnnotationArgument} `)`] - */ - def annotation(): Unit = { - qualId() - if (in.token == LPAREN) { skipAhead(); accept(RPAREN) } - else if (in.token == LBRACE) { skipAhead(); accept(RBRACE) } - } - - def modifiers(inInterface: Boolean): Modifiers = { - var flags = Flags.JavaDefined - // assumed true unless we see public/private/protected - var isPackageAccess = true - var annots: List[Tree] = Nil - def addAnnot(sym: ClassSymbol) = - annots :+= atPos(in.offset) { - in.nextToken() - New(TypeTree(sym.typeRef)) - } - - while (true) { - in.token match { - case AT if (in.lookaheadToken != INTERFACE) => - in.nextToken() - annotation() - case PUBLIC => - isPackageAccess = false - in.nextToken() - case PROTECTED => - flags |= Flags.Protected - in.nextToken() - case PRIVATE => - isPackageAccess = false - flags |= Flags.Private - in.nextToken() - case STATIC => - flags |= Flags.JavaStatic - in.nextToken() - case ABSTRACT => - flags |= Flags.Abstract - in.nextToken() - case FINAL => - flags |= Flags.Final - in.nextToken() - case DEFAULT => - flags |= Flags.DefaultMethod - in.nextToken() - case NATIVE => - addAnnot(NativeAnnot) - case TRANSIENT => - addAnnot(TransientAnnot) - case VOLATILE => - addAnnot(VolatileAnnot) - case SYNCHRONIZED | STRICTFP => - in.nextToken() - case _ => - val privateWithin: TypeName = - if (isPackageAccess && !inInterface) thisPackageName - else tpnme.EMPTY - - return Modifiers(flags, privateWithin) withAnnotations annots - } - } - assert(false, "should not be here") - throw new RuntimeException - } - - def typeParams(flags: FlagSet = Flags.JavaDefined | Flags.PrivateLocal | Flags.Param): List[TypeDef] = - if (in.token == LT) { - in.nextToken() - val tparams = repsep(() => typeParam(flags), COMMA) - acceptClosingAngle() - tparams - } else List() - - def typeParam(flags: FlagSet): TypeDef = - atPos(in.offset) { - val name = identForType() - val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else EmptyTree - TypeDef(name, TypeBoundsTree(EmptyTree, hi)).withMods(Modifiers(flags)) - } - - def bound(): Tree = - atPos(in.offset) { - val buf = ListBuffer[Tree](typ()) - while (in.token == AMP) { - in.nextToken() - buf += typ() - } - val ts = buf.toList - if (ts.tail.isEmpty) ts.head - else ts.reduce(AndTypeTree(_,_)) - } - - def formalParams(): List[ValDef] = { - accept(LPAREN) - val vparams = if (in.token == RPAREN) List() else repsep(formalParam, COMMA) - accept(RPAREN) - vparams - } - - def formalParam(): ValDef = { - val start = in.offset - if (in.token == FINAL) in.nextToken() - annotations() - var t = typ() - if (in.token == DOTDOTDOT) { - in.nextToken() - t = atPos(t.pos.start) { - PostfixOp(t, nme.raw.STAR) - } - } - atPos(start, in.offset) { - varDecl(Modifiers(Flags.JavaDefined | Flags.Param), t, ident().toTermName) - } - } - - def optThrows(): Unit = { - if (in.token == THROWS) { - in.nextToken() - repsep(typ, COMMA) - } - } - - def methodBody(): Tree = atPos(in.offset) { - skipAhead() - accept(RBRACE) // skip block - unimplementedExpr - } - - def definesInterface(token: Int) = token == INTERFACE || token == AT - - def termDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = { - val inInterface = definesInterface(parentToken) - val tparams = if (in.token == LT) typeParams(Flags.JavaDefined | Flags.Param) else List() - val isVoid = in.token == VOID - var rtpt = - if (isVoid) - atPos(in.offset) { - in.nextToken() - TypeTree(UnitType) - } - else typ() - var nameOffset = in.offset - val rtptName = rtpt match { - case Ident(name) => name - case _ => nme.EMPTY - } - if (in.token == LPAREN && rtptName != nme.EMPTY && !inInterface) { - // constructor declaration - val vparams = formalParams() - optThrows() - List { - atPos(start) { - DefDef(nme.CONSTRUCTOR, parentTParams, - List(vparams), TypeTree(), methodBody()).withMods(mods) - } - } - } else { - var mods1 = mods - if (mods is Flags.Abstract) mods1 = mods &~ Flags.Abstract - nameOffset = in.offset - val name = ident() - if (in.token == LPAREN) { - // method declaration - val vparams = formalParams() - if (!isVoid) rtpt = optArrayBrackets(rtpt) - optThrows() - val bodyOk = !inInterface || (mods is Flags.DefaultMethod) - val body = - if (bodyOk && in.token == LBRACE) { - methodBody() - } else { - if (parentToken == AT && in.token == DEFAULT) { - val annot = - atPos(nameOffset) { - New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil) - } - mods1 = mods1 withAddedAnnotation annot - val unimplemented = unimplementedExpr - skipTo(SEMI) - accept(SEMI) - unimplemented - } else { - accept(SEMI) - EmptyTree - } - } - //if (inInterface) mods1 |= Flags.Deferred - List { - atPos(start, nameOffset) { - DefDef(name.toTermName, tparams, List(vparams), rtpt, body).withMods(mods1 | Flags.Method) - } - } - } else { - if (inInterface) mods1 |= Flags.Final | Flags.JavaStatic - val result = fieldDecls(start, nameOffset, mods1, rtpt, name) - accept(SEMI) - result - } - } - } - - /** Parse a sequence of field declarations, separated by commas. - * This one is tricky because a comma might also appear in an - * initializer. Since we don't parse initializers we don't know - * what the comma signifies. - * We solve this with a second list buffer `maybe` which contains - * potential variable definitions. - * Once we have reached the end of the statement, we know whether - * these potential definitions are real or not. - */ - def fieldDecls(start: Offset, firstNameOffset: Offset, mods: Modifiers, tpt: Tree, name: Name): List[Tree] = { - val buf = ListBuffer[Tree]( - atPos(start, firstNameOffset) { varDecl(mods, tpt, name.toTermName) }) - val maybe = new ListBuffer[Tree] // potential variable definitions. - while (in.token == COMMA) { - in.nextToken() - if (in.token == IDENTIFIER) { // if there's an ident after the comma ... - val nextNameOffset = in.offset - val name = ident() - if (in.token == EQUALS || in.token == SEMI) { // ... followed by a `=` or `;`, we know it's a real variable definition - buf ++= maybe - buf += atPos(start, nextNameOffset) { varDecl(mods, tpt, name.toTermName) } - maybe.clear() - } else if (in.token == COMMA) { // ... if there's a comma after the ident, it could be a real vardef or not. - maybe += atPos(start, nextNameOffset) { varDecl(mods, tpt, name.toTermName) } - } else { // ... if there's something else we were still in the initializer of the - // previous var def; skip to next comma or semicolon. - skipTo(COMMA, SEMI) - maybe.clear() - } - } else { // ... if there's no ident following the comma we were still in the initializer of the - // previous var def; skip to next comma or semicolon. - skipTo(COMMA, SEMI) - maybe.clear() - } - } - if (in.token == SEMI) { - buf ++= maybe // every potential vardef that survived until here is real. - } - buf.toList - } - - def varDecl(mods: Modifiers, tpt: Tree, name: TermName): ValDef = { - val tpt1 = optArrayBrackets(tpt) - if (in.token == EQUALS && !(mods is Flags.Param)) skipTo(COMMA, SEMI) - val mods1 = if (mods is Flags.Final) mods else mods | Flags.Mutable - ValDef(name, tpt1, if (mods is Flags.Param) EmptyTree else unimplementedExpr).withMods(mods1) - } - - def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match { - case CLASS | ENUM | INTERFACE | AT => - typeDecl(start, if (definesInterface(parentToken)) mods | Flags.JavaStatic else mods) - case _ => - termDecl(start, mods, parentToken, parentTParams) - } - - def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree = - atPos(cdef.pos) { - assert(cdef.pos.exists) - ModuleDef(cdef.name.toTermName, - makeTemplate(List(), statics, List(), false)).withMods((cdef.mods & (Flags.AccessFlags | Flags.JavaDefined)).toTermFlags) - } - - def importCompanionObject(cdef: TypeDef): Tree = - Import(Ident(cdef.name.toTermName).withPos(NoPosition), Ident(nme.WILDCARD) :: Nil) - - // Importing the companion object members cannot be done uncritically: see - // ticket #2377 wherein a class contains two static inner classes, each of which - // has a static inner class called "Builder" - this results in an ambiguity error - // when each performs the import in the enclosing class's scope. - // - // To address this I moved the import Companion._ inside the class, as the first - // statement. This should work without compromising the enclosing scope, but may (?) - // end up suffering from the same issues it does in scala - specifically that this - // leaves auxiliary constructors unable to access members of the companion object - // as unqualified identifiers. - def addCompanionObject(statics: List[Tree], cdef: TypeDef): List[Tree] = { - // if there are no statics we can use the original cdef, but we always - // create the companion so import A._ is not an error (see ticket #1700) - val cdefNew = - if (statics.isEmpty) cdef - else { - val template = cdef.rhs.asInstanceOf[Template] - cpy.TypeDef(cdef)(cdef.name, - cpy.Template(template)(template.constr, template.parents, template.self, - importCompanionObject(cdef) :: template.body)).withMods(cdef.mods) - } - - List(makeCompanionObject(cdefNew, statics), cdefNew) - } - - def importDecl(): List[Tree] = { - val start = in.offset - accept(IMPORT) - val buf = new ListBuffer[Name] - def collectIdents() : Int = { - if (in.token == ASTERISK) { - val starOffset = in.offset - in.nextToken() - buf += nme.WILDCARD - starOffset - } else { - val nameOffset = in.offset - buf += ident() - if (in.token == DOT) { - in.nextToken() - collectIdents() - } else nameOffset - } - } - if (in.token == STATIC) in.nextToken() - else buf += nme.ROOTPKG - val lastnameOffset = collectIdents() - accept(SEMI) - val names = buf.toList - if (names.length < 2) { - syntaxError(start, "illegal import", skipIt = false) - List() - } else { - val qual = ((Ident(names.head): Tree) /: names.tail.init) (Select(_, _)) - val lastname = names.last - val ident = Ident(lastname) withPos Position(lastnameOffset) -// val selector = lastname match { -// case nme.WILDCARD => Pair(ident, Ident(null) withPos Position(-1)) -// case _ => Pair(ident, ident) -// } - val imp = atPos(start) { Import(qual, List(ident)) } - imp :: Nil - } - } - - def interfacesOpt() = - if (in.token == IMPLEMENTS) { - in.nextToken() - repsep(typ, COMMA) - } else { - List() - } - - def classDecl(start: Offset, mods: Modifiers): List[Tree] = { - accept(CLASS) - val nameOffset = in.offset - val name = identForType() - val tparams = typeParams() - val superclass = - if (in.token == EXTENDS) { - in.nextToken() - typ() - } else { - javaLangObject() - } - val interfaces = interfacesOpt() - val (statics, body) = typeBody(CLASS, name, tparams) - val cls = atPos(start, nameOffset) { - TypeDef(name, makeTemplate(superclass :: interfaces, body, tparams, true)).withMods(mods) - } - addCompanionObject(statics, cls) - } - - def interfaceDecl(start: Offset, mods: Modifiers): List[Tree] = { - accept(INTERFACE) - val nameOffset = in.offset - val name = identForType() - val tparams = typeParams() - val parents = - if (in.token == EXTENDS) { - in.nextToken() - repsep(typ, COMMA) - } else { - List(javaLangObject()) - } - val (statics, body) = typeBody(INTERFACE, name, tparams) - val iface = atPos(start, nameOffset) { - TypeDef( - name, - makeTemplate(parents, body, tparams, false)).withMods(mods | Flags.Trait | Flags.JavaInterface | Flags.Abstract) - } - addCompanionObject(statics, iface) - } - - def typeBody(leadingToken: Int, parentName: Name, parentTParams: List[TypeDef]): (List[Tree], List[Tree]) = { - accept(LBRACE) - val defs = typeBodyDecls(leadingToken, parentName, parentTParams) - accept(RBRACE) - defs - } - - def typeBodyDecls(parentToken: Int, parentName: Name, parentTParams: List[TypeDef]): (List[Tree], List[Tree]) = { - val inInterface = definesInterface(parentToken) - val statics = new ListBuffer[Tree] - val members = new ListBuffer[Tree] - while (in.token != RBRACE && in.token != EOF) { - val start = in.offset - var mods = atPos(start) { modifiers(inInterface) } - if (in.token == LBRACE) { - skipAhead() // skip init block, we just assume we have seen only static - accept(RBRACE) - } else if (in.token == SEMI) { - in.nextToken() - } else { - if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.JavaStatic - val decls = memberDecl(start, mods, parentToken, parentTParams) - (if ((mods is Flags.JavaStatic) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) - statics - else - members) ++= decls - } - } - def forwarders(sdef: Tree): List[Tree] = sdef match { - case TypeDef(name, _) if (parentToken == INTERFACE) => - var rhs: Tree = Select(Ident(parentName.toTermName), name) - List(TypeDef(name, rhs).withMods(Modifiers(Flags.Protected))) - case _ => - List() - } - val sdefs = statics.toList - val idefs = members.toList ::: (sdefs flatMap forwarders) - (sdefs, idefs) - } - def annotationParents = List( - scalaAnnotationDot(tpnme.Annotation), - Select(javaLangDot(nme.annotation), tpnme.Annotation), - scalaAnnotationDot(tpnme.ClassfileAnnotation) - ) - def annotationDecl(start: Offset, mods: Modifiers): List[Tree] = { - accept(AT) - accept(INTERFACE) - val nameOffset = in.offset - val name = identForType() - val (statics, body) = typeBody(AT, name, List()) - val constructorParams = body.collect { - case dd: DefDef => makeParam(dd.name, dd.tpt) - } - val constr = DefDef(nme.CONSTRUCTOR, - List(), List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined)) - val body1 = body.filterNot(_.isInstanceOf[DefDef]) - val templ = makeTemplate(annotationParents, constr :: body1, List(), false) - val annot = atPos(start, nameOffset) { - TypeDef(name, templ).withMods(mods | Flags.Abstract) - } - addCompanionObject(statics, annot) - } - - def enumDecl(start: Offset, mods: Modifiers): List[Tree] = { - accept(ENUM) - val nameOffset = in.offset - val name = identForType() - def enumType = Ident(name) - val interfaces = interfacesOpt() - accept(LBRACE) - val buf = new ListBuffer[Tree] - def parseEnumConsts(): Unit = { - if (in.token != RBRACE && in.token != SEMI && in.token != EOF) { - buf += enumConst(enumType) - if (in.token == COMMA) { - in.nextToken() - parseEnumConsts() - } - } - } - parseEnumConsts() - val consts = buf.toList - val (statics, body) = - if (in.token == SEMI) { - in.nextToken() - typeBodyDecls(ENUM, name, List()) - } else { - (List(), List()) - } - val predefs = List( - DefDef( - nme.values, List(), - ListOfNil, - arrayOf(enumType), - unimplementedExpr).withMods(Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method)), - DefDef( - nme.valueOf, List(), - List(List(makeParam("x".toTermName, TypeTree(StringType)))), - enumType, - unimplementedExpr).withMods(Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method))) - accept(RBRACE) - /* - val superclazz = - AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType)) - */ - val superclazz = Apply(TypeApply( - Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), - List(Literal(Constant(null)),Literal(Constant(0)))) - val enum = atPos(start, nameOffset) { - TypeDef(name, - makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.Enum) - } - addCompanionObject(consts ::: statics ::: predefs, enum) - } - - def enumConst(enumType: Tree) = { - annotations() - atPos(in.offset) { - val name = ident() - if (in.token == LPAREN) { - // skip arguments - skipAhead() - accept(RPAREN) - } - if (in.token == LBRACE) { - // skip classbody - skipAhead() - accept(RBRACE) - } - ValDef(name.toTermName, enumType, unimplementedExpr).withMods(Modifiers(Flags.Enum | Flags.Stable | Flags.JavaDefined | Flags.JavaStatic)) - } - } - - def typeDecl(start: Offset, mods: Modifiers): List[Tree] = in.token match { - case ENUM => enumDecl(start, mods) - case INTERFACE => interfaceDecl(start, mods) - case AT => annotationDecl(start, mods) - case CLASS => classDecl(start, mods) - case _ => in.nextToken(); syntaxError("illegal start of type declaration", skipIt = true); List(errorTypeTree) - } - - /** CompilationUnit ::= [package QualId semi] TopStatSeq - */ - def compilationUnit(): Tree = { - val start = in.offset - val pkg: RefTree = - if (in.token == AT || in.token == PACKAGE) { - annotations() - accept(PACKAGE) - val pkg = qualId() - accept(SEMI) - pkg - } else { - Ident(nme.EMPTY_PACKAGE) - } - thisPackageName = convertToTypeName(pkg) match { - case Some(t) => t.name.toTypeName - case _ => tpnme.EMPTY - } - val buf = new ListBuffer[Tree] - while (in.token == IMPORT) - buf ++= importDecl() - while (in.token != EOF && in.token != RBRACE) { - while (in.token == SEMI) in.nextToken() - if (in.token != EOF) { - val start = in.offset - val mods = atPos(start) { modifiers(inInterface = false) } - buf ++= typeDecl(start, mods) - } - } - val unit = atPos(start) { PackageDef(pkg, buf.toList) } - accept(EOF) - unit - } - } -} diff --git a/src/dotty/tools/dotc/parsing/JavaScanners.scala b/src/dotty/tools/dotc/parsing/JavaScanners.scala deleted file mode 100644 index 83e16627c..000000000 --- a/src/dotty/tools/dotc/parsing/JavaScanners.scala +++ /dev/null @@ -1,538 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import core.Names._, core.Contexts._, core.Decorators._, util.Positions._ -import Scanners._ -import util.SourceFile -import JavaTokens._ -import scala.annotation.{ switch, tailrec } -import scala.reflect.internal.Chars._ - -object JavaScanners { - - class JavaScanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) { - - def toToken(idx: Int): Token = - if (idx >= 0 && idx <= lastKeywordStart) kwArray(idx) else IDENTIFIER - - private class JavaTokenData0 extends TokenData - - /** we need one token lookahead - */ - val next : TokenData = new JavaTokenData0 - val prev : TokenData = new JavaTokenData0 - - // Get next token ------------------------------------------------------------ - - def nextToken(): Unit = { - if (next.token == EMPTY) { - lastOffset = lastCharOffset - fetchToken() - } - else { - this copyFrom next - next.token = EMPTY - } - } - - def lookaheadToken: Int = { - prev copyFrom this - nextToken() - val t = token - next copyFrom this - this copyFrom prev - t - } - - /** read next token - */ - private def fetchToken(): Unit = { - offset = charOffset - 1 - ch match { - case ' ' | '\t' | CR | LF | FF => - nextChar() - fetchToken() - case _ => - (ch: @switch) match { - case 'A' | 'B' | 'C' | 'D' | 'E' | - 'F' | 'G' | 'H' | 'I' | 'J' | - 'K' | 'L' | 'M' | 'N' | 'O' | - 'P' | 'Q' | 'R' | 'S' | 'T' | - 'U' | 'V' | 'W' | 'X' | 'Y' | - 'Z' | '$' | '_' | - 'a' | 'b' | 'c' | 'd' | 'e' | - 'f' | 'g' | 'h' | 'i' | 'j' | - 'k' | 'l' | 'm' | 'n' | 'o' | - 'p' | 'q' | 'r' | 's' | 't' | - 'u' | 'v' | 'w' | 'x' | 'y' | - 'z' => - putChar(ch) - nextChar() - getIdentRest() - - case '0' => - putChar(ch) - nextChar() - if (ch == 'x' || ch == 'X') { - nextChar() - base = 16 - } else { - base = 8 - } - getNumber() - - case '1' | '2' | '3' | '4' | - '5' | '6' | '7' | '8' | '9' => - base = 10 - getNumber() - - case '\"' => - nextChar() - while (ch != '\"' && (isUnicodeEscape || ch != CR && ch != LF && ch != SU)) { - getlitch() - } - if (ch == '\"') { - token = STRINGLIT - setStrVal() - nextChar() - } else { - error("unclosed string literal") - } - - case '\'' => - nextChar() - getlitch() - if (ch == '\'') { - nextChar() - token = CHARLIT - setStrVal() - } else { - error("unclosed character literal") - } - - case '=' => - token = EQUALS - nextChar() - if (ch == '=') { - token = EQEQ - nextChar() - } - - case '>' => - token = GT - nextChar() - if (ch == '=') { - token = GTEQ - nextChar() - } else if (ch == '>') { - token = GTGT - nextChar() - if (ch == '=') { - token = GTGTEQ - nextChar() - } else if (ch == '>') { - token = GTGTGT - nextChar() - if (ch == '=') { - token = GTGTGTEQ - nextChar() - } - } - } - - case '<' => - token = LT - nextChar() - if (ch == '=') { - token = LTEQ - nextChar() - } else if (ch == '<') { - token = LTLT - nextChar() - if (ch == '=') { - token = LTLTEQ - nextChar() - } - } - - case '!' => - token = BANG - nextChar() - if (ch == '=') { - token = BANGEQ - nextChar() - } - - case '~' => - token = TILDE - nextChar() - - case '?' => - token = QMARK - nextChar() - - case ':' => - token = COLON - nextChar() - - case '@' => - token = AT - nextChar() - - case '&' => - token = AMP - nextChar() - if (ch == '&') { - token = AMPAMP - nextChar() - } else if (ch == '=') { - token = AMPEQ - nextChar() - } - - case '|' => - token = BAR - nextChar() - if (ch == '|') { - token = BARBAR - nextChar() - } else if (ch == '=') { - token = BAREQ - nextChar() - } - - case '+' => - token = PLUS - nextChar() - if (ch == '+') { - token = PLUSPLUS - nextChar() - } else if (ch == '=') { - token = PLUSEQ - nextChar() - } - - case '-' => - token = MINUS - nextChar() - if (ch == '-') { - token = MINUSMINUS - nextChar() - } else if (ch == '=') { - token = MINUSEQ - nextChar() - } - - case '*' => - token = ASTERISK - nextChar() - if (ch == '=') { - token = ASTERISKEQ - nextChar() - } - - case '/' => - nextChar() - if (!skipComment()) { - token = SLASH - nextChar() - if (ch == '=') { - token = SLASHEQ - nextChar() - } - } else fetchToken() - - case '^' => - token = HAT - nextChar() - if (ch == '=') { - token = HATEQ - nextChar() - } - - case '%' => - token = PERCENT - nextChar() - if (ch == '=') { - token = PERCENTEQ - nextChar() - } - - case '.' => - token = DOT - nextChar() - if ('0' <= ch && ch <= '9') { - putChar('.'); - getFraction() - } else if (ch == '.') { - nextChar() - if (ch == '.') { - nextChar() - token = DOTDOTDOT - } else error("`.' character expected") - } - - case ';' => - token = SEMI - nextChar() - - case ',' => - token = COMMA - nextChar() - - case '(' => - token = LPAREN - nextChar() - - case '{' => - token = LBRACE - nextChar() - - case ')' => - token = RPAREN - nextChar() - - case '}' => - token = RBRACE - nextChar() - - case '[' => - token = LBRACKET - nextChar() - - case ']' => - token = RBRACKET - nextChar() - - case SU => - if (isAtEnd) token = EOF - else { - error("illegal character") - nextChar() - } - - case _ => - if (Character.isUnicodeIdentifierStart(ch)) { - putChar(ch) - nextChar() - getIdentRest() - } else { - error("illegal character: " + ch.toInt) - nextChar() - } - } - } - } - - protected def skipComment(): Boolean = { - @tailrec def skipLineComment(): Unit = ch match { - case CR | LF | SU => - case _ => nextChar(); skipLineComment() - } - @tailrec def skipJavaComment(): Unit = ch match { - case SU => incompleteInputError("unclosed comment") - case '*' => nextChar(); if (ch == '/') nextChar() else skipJavaComment() - case _ => nextChar(); skipJavaComment() - } - ch match { - case '/' => nextChar(); skipLineComment(); true - case '*' => nextChar(); skipJavaComment(); true - case _ => false - } - } - - // Identifiers --------------------------------------------------------------- - - private def getIdentRest(): Unit = { - while (true) { - (ch: @switch) match { - case 'A' | 'B' | 'C' | 'D' | 'E' | - 'F' | 'G' | 'H' | 'I' | 'J' | - 'K' | 'L' | 'M' | 'N' | 'O' | - 'P' | 'Q' | 'R' | 'S' | 'T' | - 'U' | 'V' | 'W' | 'X' | 'Y' | - 'Z' | '$' | - 'a' | 'b' | 'c' | 'd' | 'e' | - 'f' | 'g' | 'h' | 'i' | 'j' | - 'k' | 'l' | 'm' | 'n' | 'o' | - 'p' | 'q' | 'r' | 's' | 't' | - 'u' | 'v' | 'w' | 'x' | 'y' | - 'z' | - '0' | '1' | '2' | '3' | '4' | - '5' | '6' | '7' | '8' | '9' => - putChar(ch) - nextChar() - - case '_' => - putChar(ch) - nextChar() - getIdentRest() - return - case SU => - finishNamed() - return - case _ => - if (Character.isUnicodeIdentifierPart(ch)) { - putChar(ch) - nextChar() - } else { - finishNamed() - return - } - } - } - } - - // Literals ----------------------------------------------------------------- - - /** read next character in character or string literal: - */ - protected def getlitch() = - if (ch == '\\') { - nextChar() - if ('0' <= ch && ch <= '7') { - val leadch: Char = ch - var oct: Int = digit2int(ch, 8) - nextChar() - if ('0' <= ch && ch <= '7') { - oct = oct * 8 + digit2int(ch, 8) - nextChar() - if (leadch <= '3' && '0' <= ch && ch <= '7') { - oct = oct * 8 + digit2int(ch, 8) - nextChar() - } - } - putChar(oct.asInstanceOf[Char]) - } else { - ch match { - case 'b' => putChar('\b') - case 't' => putChar('\t') - case 'n' => putChar('\n') - case 'f' => putChar('\f') - case 'r' => putChar('\r') - case '\"' => putChar('\"') - case '\'' => putChar('\'') - case '\\' => putChar('\\') - case _ => - error("invalid escape character", charOffset - 1) - putChar(ch) - } - nextChar() - } - } else { - putChar(ch) - nextChar() - } - - /** read fractional part and exponent of floating point number - * if one is present. - */ - protected def getFraction(): Unit = { - token = DOUBLELIT - while ('0' <= ch && ch <= '9') { - putChar(ch) - nextChar() - } - if (ch == 'e' || ch == 'E') { - val lookahead = lookaheadReader - lookahead.nextChar() - if (lookahead.ch == '+' || lookahead.ch == '-') { - lookahead.nextChar() - } - if ('0' <= lookahead.ch && lookahead.ch <= '9') { - putChar(ch) - nextChar() - if (ch == '+' || ch == '-') { - putChar(ch) - nextChar() - } - while ('0' <= ch && ch <= '9') { - putChar(ch) - nextChar() - } - } - token = DOUBLELIT - } - if (ch == 'd' || ch == 'D') { - putChar(ch) - nextChar() - token = DOUBLELIT - } else if (ch == 'f' || ch == 'F') { - putChar(ch) - nextChar() - token = FLOATLIT - } - setStrVal() - } - - /** read a number into name and set base - */ - protected def getNumber(): Unit = { - while (digit2int(ch, if (base < 10) 10 else base) >= 0) { - putChar(ch) - nextChar() - } - token = INTLIT - if (base <= 10 && ch == '.') { - val lookahead = lookaheadReader - lookahead.nextChar() - lookahead.ch match { - case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | - '8' | '9' | 'd' | 'D' | 'e' | 'E' | 'f' | 'F' => - putChar(ch) - nextChar() - return getFraction() - case _ => - if (!isIdentifierStart(lookahead.ch)) { - putChar(ch) - nextChar() - return getFraction() - } - } - } - if (base <= 10 && - (ch == 'e' || ch == 'E' || - ch == 'f' || ch == 'F' || - ch == 'd' || ch == 'D')) { - return getFraction() - } - setStrVal() - if (ch == 'l' || ch == 'L') { - nextChar() - token = LONGLIT - } - } - - // Errors ----------------------------------------------------------------- - - override def toString() = token match { - case IDENTIFIER => - "id(" + name + ")" - case CHARLIT => - "char(" + intVal + ")" - case INTLIT => - "int(" + intVal + ")" - case LONGLIT => - "long(" + intVal + ")" - case FLOATLIT => - "float(" + floatVal + ")" - case DOUBLELIT => - "double(" + floatVal + ")" - case STRINGLIT => - "string(" + name + ")" - case SEMI => - ";" - case COMMA => - "," - case _ => - tokenString(token) - } - - /* Initialization: read first char, then first token */ - nextChar() - nextToken() - } - - val (lastKeywordStart, kwArray) = buildKeywordArray(keywords) -} diff --git a/src/dotty/tools/dotc/parsing/JavaTokens.scala b/src/dotty/tools/dotc/parsing/JavaTokens.scala deleted file mode 100644 index 9530e0516..000000000 --- a/src/dotty/tools/dotc/parsing/JavaTokens.scala +++ /dev/null @@ -1,92 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import collection.immutable.BitSet - -object JavaTokens extends TokensCommon { - final val minToken = EMPTY - final val maxToken = DOUBLE - - final val javaOnlyKeywords = tokenRange(INSTANCEOF, ASSERT) - final val sharedKeywords = BitSet( IF, FOR, ELSE, THIS, NULL, NEW, SUPER, ABSTRACT, FINAL, PRIVATE, PROTECTED, - OVERRIDE, EXTENDS, TRUE, FALSE, CLASS, IMPORT, PACKAGE, DO, THROW, TRY, CATCH, FINALLY, WHILE, RETURN ) - final val primTypes = tokenRange(VOID, DOUBLE) - final val keywords = sharedKeywords | javaOnlyKeywords | primTypes - - /** keywords */ - final val INSTANCEOF = 101; enter(INSTANCEOF, "instanceof") - final val CONST = 102; enter(CONST, "const") - - /** templates */ - final val INTERFACE = 105; enter(INTERFACE, "interface") - final val ENUM = 106; enter(ENUM, "enum") - final val IMPLEMENTS = 107; enter(IMPLEMENTS, "implements") - - /** modifiers */ - final val PUBLIC = 110; enter(PUBLIC, "public") - final val DEFAULT = 111; enter(DEFAULT, "default") - final val STATIC = 112; enter(STATIC, "static") - final val TRANSIENT = 113; enter(TRANSIENT, "transient") - final val VOLATILE = 114; enter(VOLATILE, "volatile") - final val SYNCHRONIZED = 115; enter(SYNCHRONIZED, "synchronized") - final val NATIVE = 116; enter(NATIVE, "native") - final val STRICTFP = 117; enter(STRICTFP, "strictfp") - final val THROWS = 118; enter(THROWS, "throws") - - /** control structures */ - final val BREAK = 130; enter(BREAK, "break") - final val CONTINUE = 131; enter(CONTINUE, "continue") - final val GOTO = 132; enter(GOTO, "goto") - final val SWITCH = 133; enter(SWITCH, "switch") - final val ASSERT = 134; enter(ASSERT, "assert") - - /** special symbols */ - final val EQEQ = 140 - final val BANGEQ = 141 - final val LT = 142 - final val GT = 143 - final val LTEQ = 144 - final val GTEQ = 145 - final val BANG = 146 - final val QMARK = 147 - final val AMP = 148 - final val BAR = 149 - final val PLUS = 150 - final val MINUS = 151 - final val ASTERISK = 152 - final val SLASH = 153 - final val PERCENT = 154 - final val HAT = 155 - final val LTLT = 156 - final val GTGT = 157 - final val GTGTGT = 158 - final val AMPAMP = 159 - final val BARBAR = 160 - final val PLUSPLUS = 161 - final val MINUSMINUS = 162 - final val TILDE = 163 - final val DOTDOTDOT = 164 - final val AMPEQ = 165 - final val BAREQ = 166 - final val PLUSEQ = 167 - final val MINUSEQ = 168 - final val ASTERISKEQ = 169 - final val SLASHEQ = 170 - final val PERCENTEQ = 171 - final val HATEQ = 172 - final val LTLTEQ = 173 - final val GTGTEQ = 174 - final val GTGTGTEQ = 175 - - /** primitive types */ - final val VOID = 180; enter(VOID, "void") - final val BOOLEAN = 181; enter(BOOLEAN, "boolean") - final val BYTE = 182; enter(BYTE, "byte") - final val SHORT = 183; enter(SHORT, "short") - final val CHAR = 184; enter(CHAR, "char") - final val INT = 185; enter(INT, "int") - final val LONG = 186; enter(LONG, "long") - final val FLOAT = 187; enter(FLOAT, "float") - final val DOUBLE = 188; enter(DOUBLE, "double") -} diff --git a/src/dotty/tools/dotc/parsing/MarkupParserCommon.scala b/src/dotty/tools/dotc/parsing/MarkupParserCommon.scala deleted file mode 100644 index ce2c41797..000000000 --- a/src/dotty/tools/dotc/parsing/MarkupParserCommon.scala +++ /dev/null @@ -1,257 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ -package dotty.tools.dotc -package parsing - -import Utility._ -import scala.reflect.internal.Chars.SU - - - -/** This is not a public trait - it contains common code shared - * between the library level XML parser and the compiler's. - * All members should be accessed through those. - */ -private[dotty] trait MarkupParserCommon { - protected def unreachable = scala.sys.error("Cannot be reached.") - - // type HandleType // MarkupHandler, SymbolicXMLBuilder - type InputType // Source, CharArrayReader - type PositionType // Int, Position - type ElementType // NodeSeq, Tree - type NamespaceType // NamespaceBinding, Any - type AttributesType // (MetaData, NamespaceBinding), mutable.Map[String, Tree] - - def mkAttributes(name: String, pscope: NamespaceType): AttributesType - def mkProcInstr(position: PositionType, name: String, text: String): ElementType - - /** parse a start or empty tag. - * [40] STag ::= '<' Name { S Attribute } [S] - * [44] EmptyElemTag ::= '<' Name { S Attribute } [S] - */ - protected def xTag(pscope: NamespaceType): (String, AttributesType) = { - val name = xName - xSpaceOpt - - (name, mkAttributes(name, pscope)) - } - - /** '<?' ProcInstr ::= Name [S ({Char} - ({Char}'>?' {Char})]'?>' - * - * see [15] - */ - def xProcInstr: ElementType = { - val n = xName - xSpaceOpt - xTakeUntil(mkProcInstr(_, n, _), () => tmppos, "?>") - } - - /** attribute value, terminated by either `'` or `"`. value may not contain `<`. - @param endCh either `'` or `"` - */ - def xAttributeValue(endCh: Char): String = { - val buf = new StringBuilder - while (ch != endCh) { - // well-formedness constraint - if (ch == '<') return errorAndResult("'<' not allowed in attrib value", "") - else if (ch == SU) truncatedError("") - else buf append ch_returning_nextch - } - ch_returning_nextch - // @todo: normalize attribute value - buf.toString - } - - def xAttributeValue(): String = { - val str = xAttributeValue(ch_returning_nextch) - // well-formedness constraint - normalizeAttributeValue(str) - } - - private def takeUntilChar(it: Iterator[Char], end: Char): String = { - val buf = new StringBuilder - while (it.hasNext) it.next match { - case `end` => return buf.toString - case ch => buf append ch - } - scala.sys.error("Expected '%s'".format(end)) - } - - /** [42] '<' xmlEndTag ::= '<' '/' Name S? '>' - */ - def xEndTag(startName: String): Unit = { - xToken('/') - if (xName != startName) - errorNoEnd(startName) - - xSpaceOpt - xToken('>') - } - - /** actually, Name ::= (Letter | '_' | ':') (NameChar)* but starting with ':' cannot happen - * Name ::= (Letter | '_') (NameChar)* - * - * see [5] of XML 1.0 specification - * - * pre-condition: ch != ':' // assured by definition of XMLSTART token - * post-condition: name does neither start, nor end in ':' - */ - def xName: String = { - if (ch == SU) - truncatedError("") - else if (!isNameStart(ch)) - return errorAndResult("name expected, but char '%s' cannot start a name" format ch, "") - - val buf = new StringBuilder - - do buf append ch_returning_nextch - while (isNameChar(ch)) - - if (buf.last == ':') { - reportSyntaxError( "name cannot end in ':'" ) - buf.toString dropRight 1 - } - else buf.toString - } - - private def attr_unescape(s: String) = s match { - case "lt" => "<" - case "gt" => ">" - case "amp" => "&" - case "apos" => "'" - case "quot" => "\"" - case "quote" => "\"" - case _ => "&" + s + ";" - } - - /** Replaces only character references right now. - * see spec 3.3.3 - */ - private def normalizeAttributeValue(attval: String): String = { - val buf = new StringBuilder - val it = attval.iterator.buffered - - while (it.hasNext) buf append (it.next match { - case ' ' | '\t' | '\n' | '\r' => " " - case '&' if it.head == '#' => it.next ; xCharRef(it) - case '&' => attr_unescape(takeUntilChar(it, ';')) - case c => c - }) - - buf.toString - } - - /** CharRef ::= "&#" '0'..'9' {'0'..'9'} ";" - * | "&#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";" - * - * see [66] - */ - def xCharRef(ch: () => Char, nextch: () => Unit): String = - Utility.parseCharRef(ch, nextch, reportSyntaxError _, truncatedError _) - - def xCharRef(it: Iterator[Char]): String = { - var c = it.next - Utility.parseCharRef(() => c, () => { c = it.next }, reportSyntaxError _, truncatedError _) - } - - def xCharRef: String = xCharRef(() => ch, () => nextch) - - /** Create a lookahead reader which does not influence the input */ - def lookahead(): BufferedIterator[Char] - - /** The library and compiler parsers had the interesting distinction of - * different behavior for nextch (a function for which there are a total - * of two plausible behaviors, so we know the design space was fully - * explored.) One of them returned the value of nextch before the increment - * and one of them the new value. So to unify code we have to at least - * temporarily abstract over the nextchs. - */ - def ch: Char - def nextch(): Unit - protected def ch_returning_nextch: Char - def eof: Boolean - - // def handle: HandleType - var tmppos: PositionType - - def xHandleError(that: Char, msg: String): Unit - def reportSyntaxError(str: String): Unit - def reportSyntaxError(pos: Int, str: String): Unit - - def truncatedError(msg: String): Nothing - def errorNoEnd(tag: String): Nothing - - protected def errorAndResult[T](msg: String, x: T): T = { - reportSyntaxError(msg) - x - } - - def xToken(that: Char): Unit = { - if (ch == that) nextch - else xHandleError(that, "'%s' expected instead of '%s'".format(that, ch)) - } - def xToken(that: Seq[Char]): Unit = { that foreach xToken } - - /** scan [S] '=' [S]*/ - def xEQ() = { xSpaceOpt; xToken('='); xSpaceOpt } - - /** skip optional space S? */ - def xSpaceOpt() = while (isSpace(ch) && !eof) nextch - - /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ - def xSpace() = - if (isSpace(ch)) { nextch; xSpaceOpt } - else xHandleError(ch, "whitespace expected") - - /** Apply a function and return the passed value */ - def returning[T](x: T)(f: T => Unit): T = { f(x); x } - - /** Execute body with a variable saved and restored after execution */ - def saving[A, B](getter: A, setter: A => Unit)(body: => B): B = { - val saved = getter - try body - finally setter(saved) - } - - /** Take characters from input stream until given String "until" - * is seen. Once seen, the accumulated characters are passed - * along with the current Position to the supplied handler function. - */ - protected def xTakeUntil[T]( - handler: (PositionType, String) => T, - positioner: () => PositionType, - until: String): T = - { - val sb = new StringBuilder - val head = until.head - val rest = until.tail - - while (true) { - if (ch == head && peek(rest)) - return handler(positioner(), sb.toString) - else if (ch == SU) - truncatedError("") // throws TruncatedXMLControl in compiler - - sb append ch - nextch - } - unreachable - } - - /** Create a non-destructive lookahead reader and see if the head - * of the input would match the given String. If yes, return true - * and drop the entire String from input; if no, return false - * and leave input unchanged. - */ - private def peek(lookingFor: String): Boolean = - (lookahead() take lookingFor.length sameElements lookingFor.iterator) && { - // drop the chars from the real reader (all lookahead + orig) - (0 to lookingFor.length) foreach (_ => nextch) - true - } -} diff --git a/src/dotty/tools/dotc/parsing/MarkupParsers.scala b/src/dotty/tools/dotc/parsing/MarkupParsers.scala deleted file mode 100644 index f648b9e2c..000000000 --- a/src/dotty/tools/dotc/parsing/MarkupParsers.scala +++ /dev/null @@ -1,466 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import scala.collection.mutable -import mutable.{ Buffer, ArrayBuffer, ListBuffer } -import scala.util.control.ControlThrowable -import scala.reflect.internal.Chars.SU -import Parsers._ -import util.Positions._ -import core._ -import Constants._ -import Utility._ - - -// XXX/Note: many/most of the functions in here are almost direct cut and pastes -// from another file - scala.xml.parsing.MarkupParser, it looks like. -// (It was like that when I got here.) They used to be commented "[Duplicate]" butx -// since approximately all of them were, I snipped it as noise. As far as I can -// tell this wasn't for any particularly good reason, but slightly different -// compiler and library parser interfaces meant it would take some setup. -// -// I rewrote most of these, but not as yet the library versions: so if you are -// tempted to touch any of these, please be aware of that situation and try not -// to let it get any worse. -- paulp - -/** This trait ... - * - * @author Burak Emir - * @version 1.0 - */ -object MarkupParsers { - - import ast.untpd._ - - case object MissingEndTagControl extends ControlThrowable { - override def getMessage = "start tag was here: " - } - - case object ConfusedAboutBracesControl extends ControlThrowable { - override def getMessage = " I encountered a '}' where I didn't expect one, maybe this tag isn't closed <" - } - - case object TruncatedXMLControl extends ControlThrowable { - override def getMessage = "input ended while parsing XML" - } - - class MarkupParser(parser: Parser, final val preserveWS: Boolean) extends MarkupParserCommon { - - import Tokens.{ LBRACE, RBRACE } - - type PositionType = Position - type InputType = CharArrayReader - type ElementType = Tree - type AttributesType = mutable.Map[String, Tree] - type NamespaceType = Any // namespaces ignored - - def mkAttributes(name: String, other: NamespaceType): AttributesType = xAttributes - - val eof = false - - def truncatedError(msg: String): Nothing = throw TruncatedXMLControl - def xHandleError(that: Char, msg: String) = - if (ch == SU) throw TruncatedXMLControl - else reportSyntaxError(msg) - - var input : CharArrayReader = _ - def lookahead(): BufferedIterator[Char] = - (input.buf drop input.charOffset).iterator.buffered - - import parser.{ symbXMLBuilder => handle } - - def curOffset : Int = input.charOffset - 1 - var tmppos : Position = NoPosition - def ch = input.ch - /** this method assign the next character to ch and advances in input */ - def nextch(): Unit = { input.nextChar() } - - protected def ch_returning_nextch: Char = { - val result = ch; input.nextChar(); result - } - - def mkProcInstr(position: Position, name: String, text: String): ElementType = - parser.symbXMLBuilder.procInstr(position, name, text) - - var xEmbeddedBlock = false - - private var debugLastStartElement = new mutable.Stack[(Int, String)] - private def debugLastPos = debugLastStartElement.top._1 - private def debugLastElem = debugLastStartElement.top._2 - - private def errorBraces() = { - reportSyntaxError("in XML content, please use '}}' to express '}'") - throw ConfusedAboutBracesControl - } - def errorNoEnd(tag: String) = { - reportSyntaxError("expected closing tag of " + tag) - throw MissingEndTagControl - } - - /** checks whether next character starts a Scala block, if yes, skip it. - * @return true if next character starts a scala block - */ - def xCheckEmbeddedBlock: Boolean = { - // attentions, side-effect, used in xText - xEmbeddedBlock = (ch == '{') && { nextch; (ch != '{') } - xEmbeddedBlock - } - - /** parse attribute and add it to listmap - * [41] Attributes ::= { S Name Eq AttValue } - * AttValue ::= `'` { _ } `'` - * | `"` { _ } `"` - * | `{` scalablock `}` - */ - def xAttributes = { - val aMap = mutable.LinkedHashMap[String, Tree]() - - while (isNameStart(ch)) { - val start = curOffset - val key = xName - xEQ - val delim = ch - val mid = curOffset - val value: Tree = ch match { - case '"' | '\'' => - val tmp = xAttributeValue(ch_returning_nextch) - - try handle.parseAttribute(Position(start, curOffset, mid), tmp) - catch { - case e: RuntimeException => - errorAndResult("error parsing attribute value", parser.errorTermTree) - } - - case '{' => - nextch - xEmbeddedExpr - case SU => - throw TruncatedXMLControl - case _ => - errorAndResult("' or \" delimited attribute value or '{' scala-expr '}' expected", Literal(Constant("<syntax-error>"))) - } - // well-formedness constraint: unique attribute names - if (aMap contains key) - reportSyntaxError("attribute %s may only be defined once" format key) - - aMap(key) = value - if (ch != '/' && ch != '>') - xSpace - } - aMap - } - - /** '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>' - * - * see [15] - */ - def xCharData: Tree = { - val start = curOffset - xToken("[CDATA[") - val mid = curOffset - xTakeUntil(handle.charData, () => Position(start, curOffset, mid), "]]>") - } - - def xUnparsed: Tree = { - val start = curOffset - xTakeUntil(handle.unparsed, () => Position(start, curOffset, start), "</xml:unparsed>") - } - - /** Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' - * - * see [15] - */ - def xComment: Tree = { - val start = curOffset - 2 // Rewinding to include "<!" - xToken("--") - xTakeUntil(handle.comment, () => Position(start, curOffset, start), "-->") - } - - def appendText(pos: Position, ts: Buffer[Tree], txt: String): Unit = { - def append(t: String) = ts append handle.text(pos, t) - - if (preserveWS) append(txt) - else { - val sb = new StringBuilder() - - txt foreach { c => - if (!isSpace(c)) sb append c - else if (sb.isEmpty || !isSpace(sb.last)) sb append ' ' - } - - val trimmed = sb.toString.trim - if (!trimmed.isEmpty) append(trimmed) - } - } - - /** adds entity/character to ts as side-effect - * @precond ch == '&' - */ - def content_AMP(ts: ArrayBuffer[Tree]): Unit = { - nextch - val toAppend = ch match { - case '#' => // CharacterRef - nextch - val theChar = handle.text(tmppos, xCharRef) - xToken(';') - theChar - case _ => // EntityRef - val n = xName - xToken(';') - handle.entityRef(tmppos, n) - } - - ts append toAppend - } - - /** - * @precond ch == '{' - * @postcond: xEmbeddedBlock == false! - */ - def content_BRACE(p: Position, ts: ArrayBuffer[Tree]): Unit = - if (xCheckEmbeddedBlock) ts append xEmbeddedExpr - else appendText(p, ts, xText) - - /** Returns true if it encounters an end tag (without consuming it), - * appends trees to ts as side-effect. - * - * @param ts ... - * @return ... - */ - private def content_LT(ts: ArrayBuffer[Tree]): Boolean = { - if (ch == '/') - return true // end tag - - val toAppend = ch match { - case '!' => nextch ; if (ch =='[') xCharData else xComment // CDATA or Comment - case '?' => nextch ; xProcInstr // PI - case _ => element // child node - } - - ts append toAppend - false - } - - def content: Buffer[Tree] = { - val ts = new ArrayBuffer[Tree] - while (true) { - if (xEmbeddedBlock) - ts append xEmbeddedExpr - else { - tmppos = Position(curOffset) - ch match { - // end tag, cdata, comment, pi or child node - case '<' => nextch ; if (content_LT(ts)) return ts - // either the character '{' or an embedded scala block } - case '{' => content_BRACE(tmppos, ts) // } - // EntityRef or CharRef - case '&' => content_AMP(ts) - case SU => return ts - // text content - here xEmbeddedBlock might be true - case _ => appendText(tmppos, ts, xText) - } - } - } - unreachable - } - - /** '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag - * | xmlTag1 '/' '>' - */ - def element: Tree = { - val start = curOffset - val (qname, attrMap) = xTag(()) - if (ch == '/') { // empty element - xToken("/>") - handle.element(Position(start, curOffset, start), qname, attrMap, true, new ListBuffer[Tree]) - } - else { // handle content - xToken('>') - if (qname == "xml:unparsed") - return xUnparsed - - debugLastStartElement.push((start, qname)) - val ts = content - xEndTag(qname) - debugLastStartElement.pop - val pos = Position(start, curOffset, start) - qname match { - case "xml:group" => handle.group(pos, ts) - case _ => handle.element(pos, qname, attrMap, false, ts) - } - } - } - - /** parse character data. - * precondition: xEmbeddedBlock == false (we are not in a scala block) - */ - private def xText: String = { - assert(!xEmbeddedBlock, "internal error: encountered embedded block") - val buf = new StringBuilder - def done = buf.toString - - while (ch != SU) { - if (ch == '}') { - if (charComingAfter(nextch) == '}') nextch - else errorBraces() - } - - buf append ch - nextch - if (xCheckEmbeddedBlock || ch == '<' || ch == '&') - return done - } - done - } - - /** Some try/catch/finally logic used by xLiteral and xLiteralPattern. */ - private def xLiteralCommon(f: () => Tree, ifTruncated: String => Unit): Tree = { - try return f() - catch { - case c @ TruncatedXMLControl => - ifTruncated(c.getMessage) - case c @ (MissingEndTagControl | ConfusedAboutBracesControl) => - parser.syntaxError(c.getMessage + debugLastElem + ">", debugLastPos) - case _: ArrayIndexOutOfBoundsException => - parser.syntaxError("missing end tag in XML literal for <%s>" format debugLastElem, debugLastPos) - } - finally parser.in resume Tokens.XMLSTART - - parser.errorTermTree - } - - /** Use a lookahead parser to run speculative body, and return the first char afterward. */ - private def charComingAfter(body: => Unit): Char = { - try { - input = input.lookaheadReader - body - ch - } - finally input = parser.in - } - - /** xLiteral = element { element } - * @return Scala representation of this xml literal - */ - def xLiteral: Tree = xLiteralCommon( - () => { - input = parser.in - handle.isPattern = false - - val ts = new ArrayBuffer[Tree] - val start = curOffset - tmppos = Position(curOffset) // Iuli: added this line, as it seems content_LT uses tmppos when creating trees - content_LT(ts) - - // parse more XML ? - if (charComingAfter(xSpaceOpt) == '<') { - xSpaceOpt - while (ch == '<') { - nextch - ts append element - xSpaceOpt - } - handle.makeXMLseq(Position(start, curOffset, start), ts) - } - else { - assert(ts.length == 1) - ts(0) - } - }, - msg => parser.incompleteInputError(msg) - ) - - /** @see xmlPattern. resynchronizes after successful parse - * @return this xml pattern - */ - def xLiteralPattern: Tree = xLiteralCommon( - () => { - input = parser.in - saving[Boolean, Tree](handle.isPattern, handle.isPattern = _) { - handle.isPattern = true - val tree = xPattern - xSpaceOpt - tree - } - }, - msg => parser.syntaxError(msg, curOffset) - ) - - def escapeToScala[A](op: => A, kind: String) = { - xEmbeddedBlock = false - val res = saving[List[Int], A](parser.in.sepRegions, parser.in.sepRegions = _) { - parser.in resume LBRACE - op - } - if (parser.in.token != RBRACE) - reportSyntaxError(" expected end of Scala " + kind) - - res - } - - def xEmbeddedExpr: Tree = escapeToScala(parser.block(), "block") - - /** xScalaPatterns ::= patterns - */ - def xScalaPatterns: List[Tree] = escapeToScala(parser.patterns(), "pattern") - - def reportSyntaxError(pos: Int, str: String) = parser.syntaxError(str, pos) - def reportSyntaxError(str: String): Unit = { - reportSyntaxError(curOffset, "in XML literal: " + str) - nextch() - } - - /** '<' xPattern ::= Name [S] { xmlPattern | '{' pattern3 '}' } ETag - * | Name [S] '/' '>' - */ - def xPattern: Tree = { - var start = curOffset - val qname = xName - debugLastStartElement.push((start, qname)) - xSpaceOpt - - val ts = new ArrayBuffer[Tree] - - val isEmptyTag = ch == '/' - if (isEmptyTag) nextch() - xToken('>') - - if (!isEmptyTag) { - // recurses until it hits a termination condition, then returns - def doPattern: Boolean = { - val start1 = curOffset - if (xEmbeddedBlock) ts ++= xScalaPatterns - else ch match { - case '<' => // tag - nextch - if (ch != '/') ts append xPattern // child - else return false // terminate - - case '{' => // embedded Scala patterns - while (ch == '{') { - nextch - ts ++= xScalaPatterns - } - assert(!xEmbeddedBlock, "problem with embedded block") - - case SU => - throw TruncatedXMLControl - - case _ => // text - appendText(Position(start1, curOffset, start1), ts, xText) - // here xEmbeddedBlock might be true: - // if (xEmbeddedBlock) throw new ApplicationError("after:" + text); // assert - } - true - } - - while (doPattern) { } // call until false - xEndTag(qname) - debugLastStartElement.pop - } - - handle.makeXMLpat(Position(start, curOffset, start), qname, ts) - } - } /* class MarkupParser */ -} diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala deleted file mode 100644 index fa0576c7a..000000000 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ /dev/null @@ -1,2309 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import scala.collection.mutable.ListBuffer -import scala.collection.immutable.BitSet -import util.{ SourceFile, SourcePosition } -import Tokens._ -import Scanners._ -import MarkupParsers._ -import core._ -import Flags._ -import Contexts._ -import Names._ -import ast.Positioned -import ast.Trees._ -import Decorators._ -import StdNames._ -import util.Positions._ -import Constants._ -import ScriptParsers._ -import Comments._ -import scala.annotation.{tailrec, switch} -import util.DotClass -import rewrite.Rewrites.patch - -object Parsers { - - import ast.untpd._ - import reporting.diagnostic.Message - import reporting.diagnostic.messages._ - - case class OpInfo(operand: Tree, operator: Name, offset: Offset) - - class ParensCounters { - private var parCounts = new Array[Int](lastParen - firstParen) - - def count(tok: Token) = parCounts(tok - firstParen) - def change(tok: Token, delta: Int) = parCounts(tok - firstParen) += delta - def nonePositive: Boolean = parCounts forall (_ <= 0) - } - - @sharable object Location extends Enumeration { - val InParens, InBlock, InPattern, ElseWhere = Value - } - - @sharable object ParamOwner extends Enumeration { - val Class, Type, TypeParam, Def = Value - } - - /** The parse starting point depends on whether the source file is self-contained: - * if not, the AST will be supplemented. - */ - def parser(source: SourceFile)(implicit ctx: Context) = - if (source.isSelfContained) new ScriptParser(source) - else new Parser(source) - - abstract class ParserCommon(val source: SourceFile)(implicit ctx: Context) extends DotClass { - - val in: ScannerCommon - - /* ------------- POSITIONS ------------------------------------------- */ - - /** Positions tree. - * If `t` does not have a position yet, set its position to the given one. - */ - def atPos[T <: Positioned](pos: Position)(t: T): T = - if (t.pos.isSourceDerived) t else t.withPos(pos) - - def atPos[T <: Positioned](start: Offset, point: Offset, end: Offset)(t: T): T = - atPos(Position(start, end, point))(t) - - /** If the last read offset is strictly greater than `start`, position tree - * to position spanning from `start` to last read offset, with given point. - * If the last offset is less than or equal to start, the tree `t` did not - * consume any source for its construction. In this case, don't position it yet, - * but wait for its position to be determined by `setChildPositions` when the - * parent node is positioned. - */ - def atPos[T <: Positioned](start: Offset, point: Offset)(t: T): T = - if (in.lastOffset > start) atPos(start, point, in.lastOffset)(t) else t - - def atPos[T <: Positioned](start: Offset)(t: T): T = - atPos(start, start)(t) - - def nameStart: Offset = - if (in.token == BACKQUOTED_IDENT) in.offset + 1 else in.offset - - def sourcePos(off: Int = in.offset): SourcePosition = - source atPos Position(off) - - - /* ------------- ERROR HANDLING ------------------------------------------- */ - /** The offset where the last syntax error was reported, or if a skip to a - * safepoint occurred afterwards, the offset of the safe point. - */ - protected var lastErrorOffset : Int = -1 - - /** Issue an error at given offset if beyond last error offset - * and update lastErrorOffset. - */ - def syntaxError(msg: => Message, offset: Int = in.offset): Unit = - if (offset > lastErrorOffset) { - syntaxError(msg, Position(offset)) - lastErrorOffset = in.offset - } - - /** Unconditionally issue an error at given position, without - * updating lastErrorOffset. - */ - def syntaxError(msg: => Message, pos: Position): Unit = - ctx.error(msg, source atPos pos) - - } - - class Parser(source: SourceFile)(implicit ctx: Context) extends ParserCommon(source) { - - val in: Scanner = new Scanner(source) - - val openParens = new ParensCounters - - /** This is the general parse entry point. - * Overridden by ScriptParser - */ - def parse(): Tree = { - val t = compilationUnit() - accept(EOF) - t - } - -/* -------------- TOKEN CLASSES ------------------------------------------- */ - - def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT - def isIdent(name: Name) = in.token == IDENTIFIER && in.name == name - def isSimpleLiteral = simpleLiteralTokens contains in.token - def isLiteral = literalTokens contains in.token - def isNumericLit = numericLitTokens contains in.token - def isModifier = modifierTokens contains in.token - def isExprIntro = canStartExpressionTokens contains in.token - def isTemplateIntro = templateIntroTokens contains in.token - def isDclIntro = dclIntroTokens contains in.token - def isStatSeqEnd = in.token == RBRACE || in.token == EOF - def mustStartStat = mustStartStatTokens contains in.token - - def isDefIntro(allowedMods: BitSet) = - in.token == AT || (allowedMods contains in.token) || (defIntroTokens contains in.token) - - def isStatSep: Boolean = - in.token == NEWLINE || in.token == NEWLINES || in.token == SEMI - -/* ------------- ERROR HANDLING ------------------------------------------- */ - - /** The offset of the last time when a statement on a new line was definitely - * encountered in the current scope or an outer scope. - */ - private var lastStatOffset = -1 - - def setLastStatOffset() = - if (mustStartStat && in.isAfterLineEnd) - lastStatOffset = in.offset - - /** Is offset1 less or equally indented than offset2? - * This is the case if the characters between the preceding end-of-line and offset1 - * are a prefix of the characters between the preceding end-of-line and offset2. - */ - def isLeqIndented(offset1: Int, offset2: Int): Boolean = { - def recur(idx1: Int, idx2: Int): Boolean = - idx1 == offset1 || - idx2 < offset2 && source(idx1) == source(idx2) && recur(idx1 + 1, idx2 + 1) - recur(source.startOfLine(offset1), source.startOfLine(offset2)) - } - - /** Skip on error to next safe point. - * Safe points are: - * - Closing braces, provided they match an opening brace before the error point. - * - Closing parens and brackets, provided they match an opening parent or bracket - * before the error point and there are no intervening other kinds of parens. - * - Semicolons and newlines, provided there are no intervening braces. - * - Definite statement starts on new lines, provided they are not more indented - * than the last known statement start before the error point. - */ - protected def skip(): Unit = { - val skippedParens = new ParensCounters - while (true) { - (in.token: @switch) match { - case EOF => - return - case SEMI | NEWLINE | NEWLINES => - if (skippedParens.count(LBRACE) == 0) return - case RBRACE => - if (openParens.count(LBRACE) > 0 && skippedParens.count(LBRACE) == 0) - return - skippedParens.change(LBRACE, -1) - case RPAREN => - if (openParens.count(LPAREN) > 0 && skippedParens.nonePositive) - return - skippedParens.change(LPAREN, -1) - case RBRACKET => - if (openParens.count(LBRACKET) > 0 && skippedParens.nonePositive) - return - skippedParens.change(LBRACKET, -1) - case LBRACE => - skippedParens.change(LBRACE, + 1) - case LPAREN => - skippedParens.change(LPAREN, + 1) - case LBRACKET=> - skippedParens.change(LBRACKET, + 1) - case _ => - if (mustStartStat && - in.isAfterLineEnd() && - isLeqIndented(in.offset, lastStatOffset max 0)) - return - } - in.nextToken() - } - } - - def warning(msg: => Message, sourcePos: SourcePosition) = - ctx.warning(msg, sourcePos) - - def warning(msg: => Message, offset: Int = in.offset) = - ctx.warning(msg, source atPos Position(offset)) - - def deprecationWarning(msg: => Message, offset: Int = in.offset) = - ctx.deprecationWarning(msg, source atPos Position(offset)) - - /** Issue an error at current offset taht input is incomplete */ - def incompleteInputError(msg: => Message) = - ctx.incompleteInputError(msg, source atPos Position(in.offset)) - - /** If at end of file, issue an incompleteInputError. - * Otherwise issue a syntax error and skip to next safe point. - */ - def syntaxErrorOrIncomplete(msg: => Message) = - if (in.token == EOF) incompleteInputError(msg) - else { - syntaxError(msg) - skip() - lastErrorOffset = in.offset - } // DEBUG - - private def expectedMsg(token: Int): String = - expectedMessage(showToken(token)) - private def expectedMessage(what: String): String = - s"$what expected but ${showToken(in.token)} found" - - /** Consume one token of the specified type, or - * signal an error if it is not there. - * - * @return The offset at the start of the token to accept - */ - def accept(token: Int): Int = { - val offset = in.offset - if (in.token != token) { - syntaxErrorOrIncomplete(expectedMsg(token)) - } - if (in.token == token) in.nextToken() - offset - } - - /** semi = nl {nl} | `;' - * nl = `\n' // where allowed - */ - def acceptStatSep(): Unit = in.token match { - case NEWLINE | NEWLINES => in.nextToken() - case _ => accept(SEMI) - } - - def acceptStatSepUnlessAtEnd(altEnd: Token = EOF) = - if (!isStatSeqEnd && in.token != altEnd) acceptStatSep() - - def errorTermTree = atPos(in.offset) { Literal(Constant(null)) } - - private var inFunReturnType = false - private def fromWithinReturnType[T](body: => T): T = { - val saved = inFunReturnType - try { - inFunReturnType = true - body - } finally inFunReturnType = saved - } - - def migrationWarningOrError(msg: String, offset: Int = in.offset) = - if (in.isScala2Mode) - ctx.migrationWarning(msg, source atPos Position(offset)) - else - syntaxError(msg, offset) - -/* ---------- TREE CONSTRUCTION ------------------------------------------- */ - - /** Convert tree to formal parameter list - */ - def convertToParams(tree: Tree): List[ValDef] = tree match { - case Parens(t) => convertToParam(t) :: Nil - case Tuple(ts) => ts map (convertToParam(_)) - case t => convertToParam(t) :: Nil - } - - /** Convert tree to formal parameter - */ - def convertToParam(tree: Tree, mods: Modifiers = Modifiers(), expected: String = "formal parameter"): ValDef = tree match { - case Ident(name) => - makeParameter(name.asTermName, TypeTree(), mods) withPos tree.pos - case Typed(Ident(name), tpt) => - makeParameter(name.asTermName, tpt, mods) withPos tree.pos - case _ => - syntaxError(s"not a legal $expected", tree.pos) - makeParameter(nme.ERROR, tree, mods) - } - - /** Convert (qual)ident to type identifier - */ - def convertToTypeId(tree: Tree): Tree = tree match { - case id @ Ident(name) => - cpy.Ident(id)(name.toTypeName) - case id @ Select(qual, name) => - cpy.Select(id)(qual, name.toTypeName) - case _ => - syntaxError(IdentifierExpected(tree.show), tree.pos) - tree - } - -/* --------------- PLACEHOLDERS ------------------------------------------- */ - - /** The implicit parameters introduced by `_` in the current expression. - * Parameters appear in reverse order. - */ - var placeholderParams: List[ValDef] = Nil - - def checkNoEscapingPlaceholders[T](op: => T): T = { - val savedPlaceholderParams = placeholderParams - placeholderParams = Nil - - try op - finally { - placeholderParams match { - case vd :: _ => syntaxError(UnboundPlaceholderParameter(), vd.pos) - case _ => - } - placeholderParams = savedPlaceholderParams - } - } - - def isWildcard(t: Tree): Boolean = t match { - case Ident(name1) => placeholderParams.nonEmpty && name1 == placeholderParams.head.name - case Typed(t1, _) => isWildcard(t1) - case Annotated(t1, _) => isWildcard(t1) - case Parens(t1) => isWildcard(t1) - case _ => false - } - -/* -------------- XML ---------------------------------------------------- */ - - /** the markup parser */ - lazy val xmlp = new MarkupParser(this, true) - - object symbXMLBuilder extends SymbolicXMLBuilder(this, true) // DEBUG choices - - def xmlLiteral() : Tree = xmlp.xLiteral - def xmlLiteralPattern() : Tree = xmlp.xLiteralPattern - -/* -------- COMBINATORS -------------------------------------------------------- */ - - def enclosed[T](tok: Token, body: => T): T = { - accept(tok) - openParens.change(tok, 1) - try body - finally { - accept(tok + 1) - openParens.change(tok, -1) - } - } - - def inParens[T](body: => T): T = enclosed(LPAREN, body) - def inBraces[T](body: => T): T = enclosed(LBRACE, body) - def inBrackets[T](body: => T): T = enclosed(LBRACKET, body) - - def inDefScopeBraces[T](body: => T): T = { - val saved = lastStatOffset - try inBraces(body) - finally lastStatOffset = saved - } - - /** part { `separator` part } - */ - def tokenSeparated[T](separator: Int, part: () => T): List[T] = { - val ts = new ListBuffer[T] += part() - while (in.token == separator) { - in.nextToken() - ts += part() - } - ts.toList - } - - def commaSeparated[T](part: () => T): List[T] = tokenSeparated(COMMA, part) - -/* --------- OPERAND/OPERATOR STACK --------------------------------------- */ - - var opStack: List[OpInfo] = Nil - - def checkAssoc(offset: Int, op: Name, leftAssoc: Boolean) = - if (isLeftAssoc(op) != leftAssoc) - syntaxError( - "left- and right-associative operators with same precedence may not be mixed", offset) - - def reduceStack(base: List[OpInfo], top: Tree, prec: Int, leftAssoc: Boolean): Tree = { - if (opStack != base && precedence(opStack.head.operator) == prec) - checkAssoc(opStack.head.offset, opStack.head.operator, leftAssoc) - def recur(top: Tree): Tree = { - if (opStack == base) top - else { - val opInfo = opStack.head - val opPrec = precedence(opInfo.operator) - if (prec < opPrec || leftAssoc && prec == opPrec) { - opStack = opStack.tail - recur { - val opPos = Position(opInfo.offset, opInfo.offset + opInfo.operator.length, opInfo.offset) - atPos(opPos union opInfo.operand.pos union top.pos) { - InfixOp(opInfo.operand, opInfo.operator, top) - } - } - } - else top - } - } - recur(top) - } - - /** operand { infixop operand} [postfixop], - * respecting rules of associativity and precedence. - * @param notAnOperator a token that does not count as operator. - * @param maybePostfix postfix operators are allowed. - */ - def infixOps( - first: Tree, canStartOperand: Token => Boolean, operand: () => Tree, - isType: Boolean = false, - notAnOperator: Name = nme.EMPTY, - maybePostfix: Boolean = false): Tree = { - val base = opStack - var top = first - while (isIdent && in.name != notAnOperator) { - val op = if (isType) in.name.toTypeName else in.name - top = reduceStack(base, top, precedence(op), isLeftAssoc(op)) - opStack = OpInfo(top, op, in.offset) :: opStack - ident() - newLineOptWhenFollowing(canStartOperand) - if (maybePostfix && !canStartOperand(in.token)) { - val topInfo = opStack.head - opStack = opStack.tail - val od = reduceStack(base, topInfo.operand, 0, true) - return atPos(od.pos.start, topInfo.offset) { - PostfixOp(od, topInfo.operator) - } - } - top = operand() - } - reduceStack(base, top, 0, true) - } - -/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */ - - /** Accept identifier and return its name as a term name. */ - def ident(): TermName = - if (isIdent) { - val name = in.name - in.nextToken() - name - } else { - syntaxErrorOrIncomplete(expectedMsg(IDENTIFIER)) - nme.ERROR - } - - /** Accept identifier and return Ident with its name as a term name. */ - def termIdent(): Ident = atPos(in.offset) { - makeIdent(in.token, ident()) - } - - /** Accept identifier and return Ident with its name as a type name. */ - def typeIdent(): Ident = atPos(in.offset) { - makeIdent(in.token, ident().toTypeName) - } - - private def makeIdent(tok: Token, name: Name) = - if (tok == BACKQUOTED_IDENT) BackquotedIdent(name) - else Ident(name) - - def wildcardIdent(): Ident = - atPos(accept(USCORE)) { Ident(nme.WILDCARD) } - - def termIdentOrWildcard(): Ident = - if (in.token == USCORE) wildcardIdent() else termIdent() - - /** Accept identifier acting as a selector on given tree `t`. */ - def selector(t: Tree): Tree = - atPos(t.pos.start, in.offset) { Select(t, ident()) } - - /** Selectors ::= ident { `.' ident() - * - * Accept `.' separated identifiers acting as a selectors on given tree `t`. - * @param finish An alternative parse in case the next token is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. - */ - def selectors(t: Tree, finish: Tree => Tree): Tree = { - val t1 = finish(t) - if (t1 ne t) t1 else dotSelectors(selector(t), finish) - } - - /** DotSelectors ::= { `.' ident() - * - * Accept `.' separated identifiers acting as a selectors on given tree `t`. - * @param finish An alternative parse in case the token following a `.' is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. - */ - def dotSelectors(t: Tree, finish: Tree => Tree = id) = - if (in.token == DOT) { in.nextToken(); selectors(t, finish) } - else t - - private val id: Tree => Tree = x => x - - /** Path ::= StableId - * | [Ident `.'] this - * - * @param thisOK If true, [Ident `.'] this is acceptable as the path. - * If false, another selection is required after the `this`. - * @param finish An alternative parse in case the token following a `.' is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. - */ - def path(thisOK: Boolean, finish: Tree => Tree = id): Tree = { - val start = in.offset - def handleThis(qual: Ident) = { - in.nextToken() - val t = atPos(start) { This(qual) } - if (!thisOK && in.token != DOT) syntaxError("`.' expected") - dotSelectors(t, finish) - } - def handleSuper(qual: Ident) = { - in.nextToken() - val mix = mixinQualifierOpt() - val t = atPos(start) { Super(This(qual), mix) } - accept(DOT) - dotSelectors(selector(t), finish) - } - if (in.token == THIS) handleThis(EmptyTypeIdent) - else if (in.token == SUPER) handleSuper(EmptyTypeIdent) - else { - val t = termIdent() - if (in.token == DOT) { - def qual = cpy.Ident(t)(t.name.toTypeName) - in.nextToken() - if (in.token == THIS) handleThis(qual) - else if (in.token == SUPER) handleSuper(qual) - else selectors(t, finish) - } - else t - } - } - - /** MixinQualifier ::= `[' Id `]' - */ - def mixinQualifierOpt(): Ident = - if (in.token == LBRACKET) inBrackets(atPos(in.offset) { typeIdent() }) - else EmptyTypeIdent - - /** StableId ::= Id - * | Path `.' Id - * | [id '.'] super [`[' id `]']`.' id - */ - def stableId(): Tree = - path(thisOK = false) - - /** QualId ::= Id {`.' Id} - */ - def qualId(): Tree = - dotSelectors(termIdent()) - - /** SimpleExpr ::= literal - * | symbol - * | null - * @param negOffset The offset of a preceding `-' sign, if any. - * If the literal is not negated, negOffset = in.offset. - */ - def literal(negOffset: Int = in.offset, inPattern: Boolean = false): Tree = { - def finish(value: Any): Tree = { - val t = atPos(negOffset) { Literal(Constant(value)) } - in.nextToken() - t - } - val isNegated = negOffset < in.offset - atPos(negOffset) { - if (in.token == SYMBOLLIT) atPos(in.skipToken()) { SymbolLit(in.strVal) } - else if (in.token == INTERPOLATIONID) interpolatedString() - else finish(in.token match { - case CHARLIT => in.charVal - case INTLIT => in.intVal(isNegated).toInt - case LONGLIT => in.intVal(isNegated) - case FLOATLIT => in.floatVal(isNegated).toFloat - case DOUBLELIT => in.floatVal(isNegated) - case STRINGLIT | STRINGPART => in.strVal - case TRUE => true - case FALSE => false - case NULL => null - case _ => - syntaxErrorOrIncomplete(IllegalLiteral()) - null - }) - } - } - - private def interpolatedString(inPattern: Boolean = false): Tree = atPos(in.offset) { - val segmentBuf = new ListBuffer[Tree] - val interpolator = in.name - in.nextToken() - while (in.token == STRINGPART) { - segmentBuf += Thicket( - literal(), - atPos(in.offset) { - if (in.token == IDENTIFIER) - termIdent() - else if (in.token == THIS) { - in.nextToken() - This(EmptyTypeIdent) - } - else if (in.token == LBRACE) - if (inPattern) Block(Nil, inBraces(pattern())) - else expr() - else { - ctx.error(InterpolatedStringError()) - EmptyTree - } - }) - } - if (in.token == STRINGLIT) segmentBuf += literal() - InterpolatedString(interpolator, segmentBuf.toList) - } - -/* ------------- NEW LINES ------------------------------------------------- */ - - def newLineOpt(): Unit = { - if (in.token == NEWLINE) in.nextToken() - } - - def newLinesOpt(): Unit = { - if (in.token == NEWLINE || in.token == NEWLINES) - in.nextToken() - } - - def newLineOptWhenFollowedBy(token: Int): Unit = { - // note: next is defined here because current == NEWLINE - if (in.token == NEWLINE && in.next.token == token) newLineOpt() - } - - def newLineOptWhenFollowing(p: Int => Boolean): Unit = { - // note: next is defined here because current == NEWLINE - if (in.token == NEWLINE && p(in.next.token)) newLineOpt() - } - -/* ------------- TYPES ------------------------------------------------------ */ - /** Same as [[typ]], but if this results in a wildcard it emits a syntax error and - * returns a tree for type `Any` instead. - */ - def toplevelTyp(): Tree = { - val t = typ() - findWildcardType(t) match { - case Some(wildcardPos) => - syntaxError("unbound wildcard type", wildcardPos) - scalaAny - case None => t - } - } - - /** Type ::= FunArgTypes `=>' Type - * | HkTypeParamClause `->' Type - * | InfixType - * FunArgTypes ::= InfixType - * | `(' [ FunArgType {`,' FunArgType } ] `)' - */ - def typ(): Tree = { - val start = in.offset - val t = - if (in.token == LPAREN) { - in.nextToken() - if (in.token == RPAREN) { - in.nextToken() - atPos(start, accept(ARROW)) { Function(Nil, typ()) } - } - else { - openParens.change(LPAREN, 1) - val ts = commaSeparated(funArgType) - openParens.change(LPAREN, -1) - accept(RPAREN) - if (in.token == ARROW) - atPos(start, in.skipToken()) { Function(ts, typ()) } - else { - for (t <- ts) - if (t.isInstanceOf[ByNameTypeTree]) - syntaxError(ByNameParameterNotSupported()) - val tuple = atPos(start) { makeTupleOrParens(ts) } - infixTypeRest(refinedTypeRest(withTypeRest(simpleTypeRest(tuple)))) - } - } - } - else if (in.token == LBRACKET) { - val start = in.offset - val tparams = typeParamClause(ParamOwner.TypeParam) - if (in.token == ARROW) - atPos(start, in.skipToken())(PolyTypeTree(tparams, typ())) - else { accept(ARROW); typ() } - } - else infixType() - - in.token match { - case ARROW => atPos(start, in.skipToken()) { Function(List(t), typ()) } - case FORSOME => syntaxError("existential types no longer supported; use a wildcard type or dependent type instead"); t - case _ => t - } - } - - /** InfixType ::= RefinedType {id [nl] refinedType} - */ - def infixType(): Tree = infixTypeRest(refinedType()) - - def infixTypeRest(t: Tree): Tree = - infixOps(t, canStartTypeTokens, refinedType, isType = true, notAnOperator = nme.raw.STAR) - - /** RefinedType ::= WithType {Annotation | [nl] Refinement} - */ - val refinedType: () => Tree = () => refinedTypeRest(withType()) - - def refinedTypeRest(t: Tree): Tree = { - newLineOptWhenFollowedBy(LBRACE) - if (in.token == LBRACE) refinedTypeRest(atPos(t.pos.start) { RefinedTypeTree(t, refinement()) }) - else t - } - - /** WithType ::= AnnotType {`with' AnnotType} (deprecated) - */ - def withType(): Tree = withTypeRest(annotType()) - - def withTypeRest(t: Tree): Tree = - if (in.token == WITH) { - deprecationWarning(DeprecatedWithOperator()) - in.nextToken() - AndTypeTree(t, withType()) - } - else t - - /** AnnotType ::= SimpleType {Annotation} - */ - def annotType(): Tree = annotTypeRest(simpleType()) - - def annotTypeRest(t: Tree): Tree = - if (in.token == AT) annotTypeRest(atPos(t.pos.start) { Annotated(t, annot()) }) - else t - - /** SimpleType ::= SimpleType TypeArgs - * | SimpleType `#' Id - * | StableId - * | Path `.' type - * | `(' ArgTypes `)' - * | `_' TypeBounds - * | Refinement - * | Literal - */ - def simpleType(): Tree = simpleTypeRest { - if (in.token == LPAREN) - atPos(in.offset) { makeTupleOrParens(inParens(argTypes())) } - else if (in.token == LBRACE) - atPos(in.offset) { RefinedTypeTree(EmptyTree, refinement()) } - else if (isSimpleLiteral) { SingletonTypeTree(literal()) } - else if (in.token == USCORE) { - val start = in.skipToken() - typeBounds().withPos(Position(start, in.lastOffset, start)) - } - else path(thisOK = false, handleSingletonType) match { - case r @ SingletonTypeTree(_) => r - case r => convertToTypeId(r) - } - } - - val handleSingletonType: Tree => Tree = t => - if (in.token == TYPE) { - in.nextToken() - atPos(t.pos.start) { SingletonTypeTree(t) } - } else t - - private def simpleTypeRest(t: Tree): Tree = in.token match { - case HASH => simpleTypeRest(typeProjection(t)) - case LBRACKET => simpleTypeRest(atPos(t.pos.start) { AppliedTypeTree(t, typeArgs(namedOK = true)) }) - case _ => t - } - - private def typeProjection(t: Tree): Tree = { - accept(HASH) - val id = typeIdent() - atPos(t.pos.start, id.pos.start) { Select(t, id.name) } - } - - /** NamedTypeArg ::= id `=' Type - */ - val namedTypeArg = () => { - val name = ident() - accept(EQUALS) - NamedArg(name.toTypeName, typ()) - } - - /** ArgTypes ::= Type {`,' Type} - * | NamedTypeArg {`,' NamedTypeArg} - */ - def argTypes(namedOK: Boolean = false) = { - def otherArgs(first: Tree, arg: () => Tree): List[Tree] = { - val rest = - if (in.token == COMMA) { - in.nextToken() - commaSeparated(arg) - } - else Nil - first :: rest - } - if (namedOK && in.token == IDENTIFIER) - typ() match { - case Ident(name) if in.token == EQUALS => - in.nextToken() - otherArgs(NamedArg(name, typ()), namedTypeArg) - case firstArg => - if (in.token == EQUALS) println(s"??? $firstArg") - otherArgs(firstArg, typ) - } - else commaSeparated(typ) - } - - /** FunArgType ::= Type | `=>' Type - */ - val funArgType = () => - if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(typ()) } - else typ() - - /** ParamType ::= [`=>'] ParamValueType - */ - def paramType(): Tree = - if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(paramValueType()) } - else paramValueType() - - /** ParamValueType ::= Type [`*'] - */ - def paramValueType(): Tree = { - val t = toplevelTyp() - if (isIdent(nme.raw.STAR)) { - in.nextToken() - atPos(t.pos.start) { PostfixOp(t, nme.raw.STAR) } - } else t - } - - /** TypeArgs ::= `[' Type {`,' Type} `]' - * NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]' - */ - def typeArgs(namedOK: Boolean = false): List[Tree] = inBrackets(argTypes(namedOK)) - - /** Refinement ::= `{' RefineStatSeq `}' - */ - def refinement(): List[Tree] = inBraces(refineStatSeq()) - - /** TypeBounds ::= [`>:' Type] [`<:' Type] - */ - def typeBounds(): TypeBoundsTree = - atPos(in.offset) { TypeBoundsTree(bound(SUPERTYPE), bound(SUBTYPE)) } - - private def bound(tok: Int): Tree = - if (in.token == tok) { in.nextToken(); toplevelTyp() } - else EmptyTree - - /** TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type} - */ - def typeParamBounds(pname: TypeName): Tree = { - val t = typeBounds() - val cbs = contextBounds(pname) - if (cbs.isEmpty) t - else atPos((t.pos union cbs.head.pos).start) { ContextBounds(t, cbs) } - } - - def contextBounds(pname: TypeName): List[Tree] = in.token match { - case COLON => - atPos(in.skipToken) { - AppliedTypeTree(toplevelTyp(), Ident(pname)) - } :: contextBounds(pname) - case VIEWBOUND => - deprecationWarning("view bounds `<%' are deprecated, use a context bound `:' instead") - atPos(in.skipToken) { - Function(Ident(pname) :: Nil, toplevelTyp()) - } :: contextBounds(pname) - case _ => - Nil - } - - def typedOpt(): Tree = - if (in.token == COLON) { in.nextToken(); toplevelTyp() } - else TypeTree() - - def typeDependingOn(location: Location.Value): Tree = - if (location == Location.InParens) typ() - else if (location == Location.InPattern) refinedType() - else infixType() - - /** Checks whether `t` is a wildcard type. - * If it is, returns the [[Position]] where the wildcard occurs. - */ - @tailrec - private final def findWildcardType(t: Tree): Option[Position] = t match { - case TypeBoundsTree(_, _) => Some(t.pos) - case Parens(t1) => findWildcardType(t1) - case Annotated(t1, _) => findWildcardType(t1) - case _ => None - } - -/* ----------- EXPRESSIONS ------------------------------------------------ */ - - /** EqualsExpr ::= `=' Expr - */ - def equalsExpr(): Tree = { - accept(EQUALS) - expr() - } - - def condExpr(altToken: Token): Tree = { - if (in.token == LPAREN) { - val t = atPos(in.offset) { Parens(inParens(exprInParens())) } - if (in.token == altToken) in.nextToken() - t - } else { - val t = expr() - accept(altToken) - t - } - } - - /** Expr ::= FunParams `=>' Expr - * | Expr1 - * FunParams ::= Bindings - * | [`implicit'] Id - * | `_' - * ExprInParens ::= PostfixExpr `:' Type - * | Expr - * BlockResult ::= (FunParams | [`implicit'] Id `:' InfixType) => Block - * | Expr1 - * Expr1 ::= `if' `(' Expr `)' {nl} Expr [[semi] else Expr] - * | `if' Expr `then' Expr [[semi] else Expr] - * | `while' `(' Expr `)' {nl} Expr - * | `while' Expr `do' Expr - * | `do' Expr [semi] `while' Expr - * | `try' Expr Catches [`finally' Expr] - * | `try' Expr [`finally' Expr] - * | `throw' Expr - * | `return' [Expr] - * | ForExpr - * | [SimpleExpr `.'] Id `=' Expr - * | SimpleExpr1 ArgumentExprs `=' Expr - * | PostfixExpr [Ascription] - * | PostfixExpr `match' `{' CaseClauses `}' - * Bindings ::= `(' [Binding {`,' Binding}] `)' - * Binding ::= (Id | `_') [`:' Type] - * Ascription ::= `:' CompoundType - * | `:' Annotation {Annotation} - * | `:' `_' `*' - */ - val exprInParens = () => expr(Location.InParens) - - def expr(): Tree = expr(Location.ElseWhere) - - def expr(location: Location.Value): Tree = { - val saved = placeholderParams - placeholderParams = Nil - val t = expr1(location) - if (in.token == ARROW) { - placeholderParams = saved - closureRest(t.pos.start, location, convertToParams(t)) - } - else if (isWildcard(t)) { - placeholderParams = placeholderParams ::: saved - t - } - else - try - if (placeholderParams.isEmpty) t - else new WildcardFunction(placeholderParams.reverse, t) - finally placeholderParams = saved - } - - def expr1(location: Location.Value = Location.ElseWhere): Tree = in.token match { - case IF => - atPos(in.skipToken()) { - val cond = condExpr(THEN) - newLinesOpt() - val thenp = expr() - val elsep = if (in.token == ELSE) { in.nextToken(); expr() } - else EmptyTree - If(cond, thenp, elsep) - } - case WHILE => - atPos(in.skipToken()) { - val cond = condExpr(DO) - newLinesOpt() - val body = expr() - WhileDo(cond, body) - } - case DO => - atPos(in.skipToken()) { - val body = expr() - if (isStatSep) in.nextToken() - accept(WHILE) - val cond = expr() - DoWhile(body, cond) - } - case TRY => - val tryOffset = in.offset - atPos(in.skipToken()) { - val body = expr() - val (handler, handlerStart) = - if (in.token == CATCH) { - val pos = in.offset - in.nextToken() - (expr(), pos) - } else (EmptyTree, -1) - - handler match { - case Block(Nil, EmptyTree) => - assert(handlerStart != -1) - syntaxError( - new EmptyCatchBlock(body), - Position(handlerStart, handler.pos.end) - ) - case _ => - } - - val finalizer = - if (in.token == FINALLY) { accept(FINALLY); expr() } - else { - if (handler.isEmpty) warning( - EmptyCatchAndFinallyBlock(body), - source atPos Position(tryOffset, body.pos.end) - ) - EmptyTree - } - ParsedTry(body, handler, finalizer) - } - case THROW => - atPos(in.skipToken()) { Throw(expr()) } - case RETURN => - atPos(in.skipToken()) { Return(if (isExprIntro) expr() else EmptyTree, EmptyTree) } - case FOR => - forExpr() - case IMPLICIT => - implicitClosure(in.skipToken(), location) - case _ => - expr1Rest(postfixExpr(), location) - } - - def expr1Rest(t: Tree, location: Location.Value) = in.token match { - case EQUALS => - t match { - case Ident(_) | Select(_, _) | Apply(_, _) => - atPos(t.pos.start, in.skipToken()) { Assign(t, expr()) } - case _ => - t - } - case COLON => - ascription(t, location) - case MATCH => - atPos(t.pos.start, in.skipToken()) { - inBraces(Match(t, caseClauses())) - } - case _ => - t - } - - def ascription(t: Tree, location: Location.Value) = atPos(t.pos.start, in.skipToken()) { - in.token match { - case USCORE => - val uscoreStart = in.skipToken() - if (isIdent(nme.raw.STAR)) { - in.nextToken() - if (in.token != RPAREN) syntaxError(SeqWildcardPatternPos(), uscoreStart) - Typed(t, atPos(uscoreStart) { Ident(tpnme.WILDCARD_STAR) }) - } else { - syntaxErrorOrIncomplete(IncorrectRepeatedParameterSyntax()) - t - } - case AT if location != Location.InPattern => - (t /: annotations())(Annotated) - case _ => - val tpt = typeDependingOn(location) - if (isWildcard(t) && location != Location.InPattern) { - val vd :: rest = placeholderParams - placeholderParams = - cpy.ValDef(vd)(tpt = tpt).withPos(vd.pos union tpt.pos) :: rest - } - Typed(t, tpt) - } - } - - /** Expr ::= implicit Id `=>' Expr - * BlockResult ::= implicit Id [`:' InfixType] `=>' Block - */ - def implicitClosure(start: Int, location: Location.Value, implicitMod: Option[Mod] = None): Tree = { - var mods = atPos(start) { Modifiers(Implicit) } - if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get) - val id = termIdent() - val paramExpr = - if (location == Location.InBlock && in.token == COLON) - atPos(id.pos.start, in.skipToken()) { Typed(id, infixType()) } - else - id - closureRest(start, location, convertToParam(paramExpr, mods) :: Nil) - } - - def closureRest(start: Int, location: Location.Value, params: List[Tree]): Tree = - atPos(start, in.offset) { - accept(ARROW) - Function(params, if (location == Location.InBlock) block() else expr()) - } - - /** PostfixExpr ::= InfixExpr [Id [nl]] - * InfixExpr ::= PrefixExpr - * | InfixExpr Id [nl] InfixExpr - */ - def postfixExpr(): Tree = - infixOps(prefixExpr(), canStartExpressionTokens, prefixExpr, maybePostfix = true) - - /** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr - */ - val prefixExpr = () => - if (isIdent && nme.raw.isUnary(in.name)) { - val start = in.offset - val name = ident() - if (name == nme.raw.MINUS && isNumericLit) - simpleExprRest(literal(start), canApply = true) - else - atPos(start) { PrefixOp(name, simpleExpr()) } - } - else simpleExpr() - - /** SimpleExpr ::= new Template - * | BlockExpr - * | SimpleExpr1 [`_'] - * SimpleExpr1 ::= literal - * | xmlLiteral - * | Path - * | `(' [ExprsInParens] `)' - * | SimpleExpr `.' Id - * | SimpleExpr (TypeArgs | NamedTypeArgs) - * | SimpleExpr1 ArgumentExprs - */ - def simpleExpr(): Tree = { - var canApply = true - val t = in.token match { - case XMLSTART => - xmlLiteral() - case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER => - path(thisOK = true) - case USCORE => - val start = in.skipToken() - val pname = ctx.freshName(nme.USCORE_PARAM_PREFIX).toTermName - val param = ValDef(pname, TypeTree(), EmptyTree).withFlags(SyntheticTermParam) - .withPos(Position(start)) - placeholderParams = param :: placeholderParams - atPos(start) { Ident(pname) } - case LPAREN => - atPos(in.offset) { makeTupleOrParens(inParens(exprsInParensOpt())) } - case LBRACE => - canApply = false - blockExpr() - case NEW => - canApply = false - val start = in.skipToken() - val (impl, missingBody) = template(emptyConstructor) - impl.parents match { - case parent :: Nil if missingBody => - if (parent.isType) ensureApplied(wrapNew(parent)) else parent - case _ => - New(impl.withPos(Position(start, in.lastOffset))) - } - case _ => - if (isLiteral) literal() - else { - syntaxErrorOrIncomplete(IllegalStartSimpleExpr(tokenString(in.token))) - errorTermTree - } - } - simpleExprRest(t, canApply) - } - - def simpleExprRest(t: Tree, canApply: Boolean = true): Tree = { - if (canApply) newLineOptWhenFollowedBy(LBRACE) - in.token match { - case DOT => - in.nextToken() - simpleExprRest(selector(t), canApply = true) - case LBRACKET => - val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs(namedOK = true)) } - simpleExprRest(tapp, canApply = true) - case LPAREN | LBRACE if canApply => - val app = atPos(t.pos.start, in.offset) { Apply(t, argumentExprs()) } - simpleExprRest(app, canApply = true) - case USCORE => - atPos(t.pos.start, in.skipToken()) { PostfixOp(t, nme.WILDCARD) } - case _ => - t - } - } - - /** ExprsInParens ::= ExprInParens {`,' ExprInParens} - */ - def exprsInParensOpt(): List[Tree] = - if (in.token == RPAREN) Nil else commaSeparated(exprInParens) - - /** ParArgumentExprs ::= `(' [ExprsInParens] `)' - * | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')' \ - */ - def parArgumentExprs(): List[Tree] = - inParens(if (in.token == RPAREN) Nil else commaSeparated(argumentExpr)) - - /** ArgumentExprs ::= ParArgumentExprs - * | [nl] BlockExpr - */ - def argumentExprs(): List[Tree] = - if (in.token == LBRACE) blockExpr() :: Nil else parArgumentExprs() - - val argumentExpr = () => exprInParens() match { - case a @ Assign(Ident(id), rhs) => cpy.NamedArg(a)(id, rhs) - case e => e - } - - /** ArgumentExprss ::= {ArgumentExprs} - */ - def argumentExprss(fn: Tree): Tree = { - newLineOptWhenFollowedBy(LBRACE) - if (in.token == LPAREN || in.token == LBRACE) argumentExprss(Apply(fn, argumentExprs())) - else fn - } - - /** ParArgumentExprss ::= {ParArgumentExprs} - */ - def parArgumentExprss(fn: Tree): Tree = - if (in.token == LPAREN) parArgumentExprss(Apply(fn, parArgumentExprs())) - else fn - - /** BlockExpr ::= `{' (CaseClauses | Block) `}' - */ - def blockExpr(): Tree = atPos(in.offset) { - inDefScopeBraces { - if (in.token == CASE) Match(EmptyTree, caseClauses()) - else block() - } - } - - /** Block ::= BlockStatSeq - * @note Return tree does not carry source position. - */ - def block(): Tree = { - val stats = blockStatSeq() - def isExpr(stat: Tree) = !(stat.isDef || stat.isInstanceOf[Import]) - if (stats.nonEmpty && isExpr(stats.last)) Block(stats.init, stats.last) - else Block(stats, EmptyTree) - } - - /** Guard ::= if PostfixExpr - */ - def guard(): Tree = - if (in.token == IF) { in.nextToken(); postfixExpr() } - else EmptyTree - - /** Enumerators ::= Generator {semi Enumerator | Guard} - */ - def enumerators(): List[Tree] = generator() :: enumeratorsRest() - - def enumeratorsRest(): List[Tree] = - if (isStatSep) { in.nextToken(); enumerator() :: enumeratorsRest() } - else if (in.token == IF) guard() :: enumeratorsRest() - else Nil - - /** Enumerator ::= Generator - * | Guard - * | Pattern1 `=' Expr - */ - def enumerator(): Tree = - if (in.token == IF) guard() - else { - val pat = pattern1() - if (in.token == EQUALS) atPos(pat.pos.start, in.skipToken()) { GenAlias(pat, expr()) } - else generatorRest(pat) - } - - /** Generator ::= Pattern `<-' Expr - */ - def generator(): Tree = generatorRest(pattern1()) - - def generatorRest(pat: Tree) = - atPos(pat.pos.start, accept(LARROW)) { GenFrom(pat, expr()) } - - /** ForExpr ::= `for' (`(' Enumerators `)' | `{' Enumerators `}') - * {nl} [`yield'] Expr - * | `for' Enumerators (`do' Expr | `yield' Expr) - */ - def forExpr(): Tree = atPos(in.skipToken()) { - var wrappedEnums = true - val enums = - if (in.token == LBRACE) inBraces(enumerators()) - else if (in.token == LPAREN) { - val lparenOffset = in.skipToken() - openParens.change(LPAREN, 1) - val pats = patternsOpt() - val pat = - if (in.token == RPAREN || pats.length > 1) { - wrappedEnums = false - accept(RPAREN) - openParens.change(LPAREN, -1) - atPos(lparenOffset) { makeTupleOrParens(pats) } // note: alternatives `|' need to be weeded out by typer. - } - else pats.head - val res = generatorRest(pat) :: enumeratorsRest() - if (wrappedEnums) { - accept(RPAREN) - openParens.change(LPAREN, -1) - } - res - } else { - wrappedEnums = false - enumerators() - } - newLinesOpt() - if (in.token == YIELD) { in.nextToken(); ForYield(enums, expr()) } - else if (in.token == DO) { in.nextToken(); ForDo(enums, expr()) } - else { - if (!wrappedEnums) syntaxErrorOrIncomplete(YieldOrDoExpectedInForComprehension()) - ForDo(enums, expr()) - } - } - - /** CaseClauses ::= CaseClause {CaseClause} - */ - def caseClauses(): List[CaseDef] = { - val buf = new ListBuffer[CaseDef] - buf += caseClause() - while (in.token == CASE) buf += caseClause() - buf.toList - } - - /** CaseClause ::= case Pattern [Guard] `=>' Block - */ - def caseClause(): CaseDef = atPos(in.offset) { - accept(CASE) - CaseDef(pattern(), guard(), atPos(accept(ARROW)) { block() }) - } - - /* -------- PATTERNS ------------------------------------------- */ - - /** Pattern ::= Pattern1 { `|' Pattern1 } - */ - val pattern = () => { - val pat = pattern1() - if (isIdent(nme.raw.BAR)) - atPos(pat.pos.start) { Alternative(pat :: patternAlts()) } - else pat - } - - def patternAlts(): List[Tree] = - if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1() :: patternAlts() } - else Nil - - /** Pattern1 ::= PatVar Ascription - * | Pattern2 - */ - def pattern1(): Tree = { - val p = pattern2() - if (isVarPattern(p) && in.token == COLON) ascription(p, Location.InPattern) - else p - } - - /** Pattern2 ::= [varid `@'] InfixPattern - */ - val pattern2 = () => infixPattern() match { - case p @ Ident(name) if isVarPattern(p) && in.token == AT => - val offset = in.skipToken() - - // compatibility for Scala2 `x @ _*` syntax - infixPattern() match { - case pt @ Ident(tpnme.WILDCARD_STAR) => - migrationWarningOrError("The syntax `x @ _*' is no longer supported; use `x : _*' instead", p.pos.start) - atPos(p.pos.start, offset) { Typed(p, pt) } - case p => - atPos(p.pos.start, offset) { Bind(name, p) } - } - case p @ Ident(tpnme.WILDCARD_STAR) => - // compatibility for Scala2 `_*` syntax - migrationWarningOrError("The syntax `_*' is no longer supported; use `x : _*' instead", p.pos.start) - atPos(p.pos.start) { Typed(Ident(nme.WILDCARD), p) } - case p => - p - } - - /** InfixPattern ::= SimplePattern {Id [nl] SimplePattern} - */ - def infixPattern(): Tree = - infixOps(simplePattern(), canStartExpressionTokens, simplePattern, notAnOperator = nme.raw.BAR) - - /** SimplePattern ::= PatVar - * | Literal - * | XmlPattern - * | `(' [Patterns] `)' - * | SimplePattern1 [TypeArgs] [ArgumentPatterns] - * SimplePattern1 ::= Path - * | `{' Block `}' - * | SimplePattern1 `.' Id - * PatVar ::= Id - * | `_' - */ - val simplePattern = () => in.token match { - case IDENTIFIER | BACKQUOTED_IDENT | THIS => - path(thisOK = true) match { - case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(id.pos.start) - case t => simplePatternRest(t) - } - case USCORE => - val wildIndent = wildcardIdent() - - // compatibility for Scala2 `x @ _*` and `_*` syntax - // `x: _*' is parsed in `ascription' - if (isIdent(nme.raw.STAR)) { - in.nextToken() - if (in.token != RPAREN) syntaxError(SeqWildcardPatternPos(), wildIndent.pos) - atPos(wildIndent.pos) { Ident(tpnme.WILDCARD_STAR) } - } else wildIndent - case LPAREN => - atPos(in.offset) { makeTupleOrParens(inParens(patternsOpt())) } - case LBRACE => - dotSelectors(blockExpr()) - case XMLSTART => - xmlLiteralPattern() - case _ => - if (isLiteral) literal() - else { - syntaxErrorOrIncomplete(IllegalStartOfSimplePattern()) - errorTermTree - } - } - - def simplePatternRest(t: Tree): Tree = { - var p = t - if (in.token == LBRACKET) - p = atPos(t.pos.start, in.offset) { TypeApply(p, typeArgs()) } - if (in.token == LPAREN) - p = atPos(t.pos.start, in.offset) { Apply(p, argumentPatterns()) } - p - } - - /** Patterns ::= Pattern [`,' Pattern] - */ - def patterns() = commaSeparated(pattern) - - def patternsOpt(): List[Tree] = - if (in.token == RPAREN) Nil else patterns() - - - /** ArgumentPatterns ::= `(' [Patterns] `)' - * | `(' [Patterns `,'] Pattern2 `:' `_' `*' ') - */ - def argumentPatterns(): List[Tree] = - inParens(patternsOpt) - -/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */ - - private def modOfToken(tok: Int): Mod = tok match { - case ABSTRACT => Mod.Abstract() - case FINAL => Mod.Final() - case IMPLICIT => Mod.Implicit(ImplicitCommon) - case INLINE => Mod.Inline() - case LAZY => Mod.Lazy() - case OVERRIDE => Mod.Override() - case PRIVATE => Mod.Private() - case PROTECTED => Mod.Protected() - case SEALED => Mod.Sealed() - } - - /** Drop `private' modifier when followed by a qualifier. - * Contract `abstract' and `override' to ABSOVERRIDE - */ - private def normalize(mods: Modifiers): Modifiers = - if ((mods is Private) && mods.hasPrivateWithin) - normalize(mods &~ Private) - else if (mods is AbstractAndOverride) - normalize(addFlag(mods &~ (Abstract | Override), AbsOverride)) - else - mods - - private def addModifier(mods: Modifiers): Modifiers = { - val tok = in.token - val mod = atPos(in.skipToken()) { modOfToken(tok) } - - if (mods is mod.flags) syntaxError(RepeatedModifier(mod.flags.toString)) - addMod(mods, mod) - } - - private def compatible(flags1: FlagSet, flags2: FlagSet): Boolean = ( - flags1.isEmpty - || flags2.isEmpty - || flags1.isTermFlags && flags2.isTermFlags - || flags1.isTypeFlags && flags2.isTypeFlags - ) - - def addFlag(mods: Modifiers, flag: FlagSet): Modifiers = { - def incompatible(kind: String) = { - syntaxError(s"modifier(s) `${mods.flags}' not allowed for $kind") - Modifiers(flag) - } - if (compatible(mods.flags, flag)) mods | flag - else flag match { - case Trait => incompatible("trait") - case Method => incompatible("method") - case Mutable => incompatible("variable") - case _ => - syntaxError(s"illegal modifier combination: ${mods.flags} and $flag") - mods - } - } - - /** Always add the syntactic `mod`, but check and conditionally add semantic `mod.flags` - */ - def addMod(mods: Modifiers, mod: Mod): Modifiers = - addFlag(mods, mod.flags).withAddedMod(mod) - - /** AccessQualifier ::= "[" (Id | this) "]" - */ - def accessQualifierOpt(mods: Modifiers): Modifiers = - if (in.token == LBRACKET) { - if ((mods is Local) || mods.hasPrivateWithin) - syntaxError("duplicate private/protected qualifier") - inBrackets { - if (in.token == THIS) { in.nextToken(); mods | Local } - else mods.withPrivateWithin(ident().toTypeName) - } - } else mods - - /** {Annotation} {Modifier} - * Modifiers ::= {Modifier} - * LocalModifiers ::= {LocalModifier} - * AccessModifier ::= (private | protected) [AccessQualifier] - * Modifier ::= LocalModifier - * | AccessModifier - * | override - * LocalModifier ::= abstract | final | sealed | implicit | lazy - */ - def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = { - def loop(mods: Modifiers): Modifiers = { - if (allowed contains in.token) { - val isAccessMod = accessModifierTokens contains in.token - val mods1 = addModifier(mods) - loop(if (isAccessMod) accessQualifierOpt(mods1) else mods1) - } else if (in.token == NEWLINE && (mods.hasFlags || mods.hasAnnotations)) { - in.nextToken() - loop(mods) - } else { - mods - } - } - normalize(loop(start)) - } - - /** Wrap annotation or constructor in New(...).<init> */ - def wrapNew(tpt: Tree) = Select(New(tpt), nme.CONSTRUCTOR) - - /** Adjust start of annotation or constructor to position of preceding @ or new */ - def adjustStart(start: Offset)(tree: Tree): Tree = { - val tree1 = tree match { - case Apply(fn, args) => cpy.Apply(tree)(adjustStart(start)(fn), args) - case Select(qual, name) => cpy.Select(tree)(adjustStart(start)(qual), name) - case _ => tree - } - if (start < tree1.pos.start) tree1.withPos(tree1.pos.withStart(start)) - else tree1 - } - - /** Annotation ::= `@' SimpleType {ParArgumentExprs} - */ - def annot() = - adjustStart(accept(AT)) { - if (in.token == INLINE) in.token = BACKQUOTED_IDENT // allow for now - ensureApplied(parArgumentExprss(wrapNew(simpleType()))) - } - - def annotations(skipNewLines: Boolean = false): List[Tree] = { - if (skipNewLines) newLineOptWhenFollowedBy(AT) - if (in.token == AT) annot() :: annotations(skipNewLines) - else Nil - } - - def annotsAsMods(skipNewLines: Boolean = false): Modifiers = - Modifiers() withAnnotations annotations(skipNewLines) - - def defAnnotsMods(allowed: BitSet): Modifiers = - modifiers(allowed, annotsAsMods(skipNewLines = true)) - - /* -------- PARAMETERS ------------------------------------------- */ - - /** ClsTypeParamClause::= `[' ClsTypeParam {`,' ClsTypeParam} `]' - * ClsTypeParam ::= {Annotation} [{Modifier} type] [`+' | `-'] - * Id [HkTypeParamClause] TypeParamBounds - * - * DefTypeParamClause::= `[' DefTypeParam {`,' DefTypeParam} `]' - * DefTypeParam ::= {Annotation} Id [HkTypeParamClause] TypeParamBounds - * - * TypTypeParamCaluse::= `[' TypTypeParam {`,' TypTypeParam} `]' - * TypTypeParam ::= {Annotation} Id [HkTypePamClause] TypeBounds - * - * HkTypeParamClause ::= `[' HkTypeParam {`,' HkTypeParam} `]' - * HkTypeParam ::= {Annotation} ['+' | `-'] (Id[HkTypePamClause] | _') TypeBounds - */ - def typeParamClause(ownerKind: ParamOwner.Value): List[TypeDef] = inBrackets { - def typeParam(): TypeDef = { - val isConcreteOwner = ownerKind == ParamOwner.Class || ownerKind == ParamOwner.Def - val start = in.offset - var mods = annotsAsMods() - if (ownerKind == ParamOwner.Class) { - mods = modifiers(start = mods) - mods = - atPos(start, in.offset) { - if (in.token == TYPE) { - val mod = atPos(in.skipToken()) { Mod.Type() } - (mods | Param | ParamAccessor).withAddedMod(mod) - } else { - if (mods.hasFlags) syntaxError(TypeParamsTypeExpected(mods, ident())) - mods | Param | PrivateLocal - } - } - } - else mods = atPos(start) (mods | Param) - if (ownerKind != ParamOwner.Def) { - if (isIdent(nme.raw.PLUS)) mods |= Covariant - else if (isIdent(nme.raw.MINUS)) mods |= Contravariant - if (mods is VarianceFlags) in.nextToken() - } - atPos(start, nameStart) { - val name = - if (isConcreteOwner || in.token != USCORE) ident().toTypeName - else { - in.nextToken() - ctx.freshName(nme.USCORE_PARAM_PREFIX).toTypeName - } - val hkparams = typeParamClauseOpt(ParamOwner.TypeParam) - val bounds = - if (isConcreteOwner) typeParamBounds(name) - else typeBounds() - TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods) - } - } - commaSeparated(typeParam) - } - - def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] = - if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil - - /** ClsParamClauses ::= {ClsParamClause} [[nl] `(' `implicit' ClsParams `)'] - * ClsParamClause ::= [nl] `(' [ClsParams] ')' - * ClsParams ::= ClsParam {`' ClsParam} - * ClsParam ::= {Annotation} [{Modifier} (`val' | `var') | `inline'] Param - * DefParamClauses ::= {DefParamClause} [[nl] `(' `implicit' DefParams `)'] - * DefParamClause ::= [nl] `(' [DefParams] ')' - * DefParams ::= DefParam {`,' DefParam} - * DefParam ::= {Annotation} [`inline'] Param - * Param ::= id `:' ParamType [`=' Expr] - */ - def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = { - var implicitMod: Mod = null - var firstClauseOfCaseClass = ofCaseClass - var implicitOffset = -1 // use once - def param(): ValDef = { - val start = in.offset - var mods = annotsAsMods() - if (owner.isTypeName) { - mods = modifiers(start = mods) | ParamAccessor - mods = - atPos(start, in.offset) { - if (in.token == VAL) { - val mod = atPos(in.skipToken()) { Mod.Val() } - mods.withAddedMod(mod) - } else if (in.token == VAR) { - val mod = atPos(in.skipToken()) { Mod.Var() } - addMod(mods, mod) - } else { - if (!(mods.flags &~ (ParamAccessor | Inline)).isEmpty) - syntaxError("`val' or `var' expected") - if (firstClauseOfCaseClass) mods else mods | PrivateLocal - } - } - } - else { - if (in.token == INLINE) mods = addModifier(mods) - mods = atPos(start) { mods | Param } - } - atPos(start, nameStart) { - val name = ident() - val tpt = - if (ctx.settings.YmethodInfer.value && owner.isTermName && in.token != COLON) { - TypeTree() // XX-METHOD-INFER - } else { - accept(COLON) - if (in.token == ARROW) { - if (owner.isTypeName && !(mods is Local)) - syntaxError(s"${if (mods is Mutable) "`var'" else "`val'"} parameters may not be call-by-name") - else if (implicitMod != null) - syntaxError("implicit parameters may not be call-by-name") - } - paramType() - } - val default = - if (in.token == EQUALS) { in.nextToken(); expr() } - else EmptyTree - if (implicitOffset >= 0) { - mods = mods.withPos(mods.pos.union(Position(implicitOffset, implicitOffset))) - implicitOffset = -1 - } - if (implicitMod != null) mods = addMod(mods, implicitMod) - ValDef(name, tpt, default).withMods(mods) - } - } - def paramClause(): List[ValDef] = inParens { - if (in.token == RPAREN) Nil - else { - if (in.token == IMPLICIT) { - implicitOffset = in.offset - implicitMod = atPos(in.skipToken()) { Mod.Implicit(Implicit) } - } - commaSeparated(param) - } - } - def clauses(): List[List[ValDef]] = { - newLineOptWhenFollowedBy(LPAREN) - if (in.token == LPAREN) - paramClause() :: { - firstClauseOfCaseClass = false - if (implicitMod == null) clauses() else Nil - } - else Nil - } - val start = in.offset - val result = clauses() - if (owner == nme.CONSTRUCTOR && (result.isEmpty || (result.head take 1 exists (_.mods is Implicit)))) { - in.token match { - case LBRACKET => syntaxError("no type parameters allowed here") - case EOF => incompleteInputError(AuxConstructorNeedsNonImplicitParameter()) - case _ => syntaxError(AuxConstructorNeedsNonImplicitParameter(), start) - } - } - result - } - -/* -------- DEFS ------------------------------------------- */ - - /** Import ::= import ImportExpr {`,' ImportExpr} - */ - def importClause(): List[Tree] = { - val offset = accept(IMPORT) - commaSeparated(importExpr) match { - case t :: rest => - // The first import should start at the position of the keyword. - t.withPos(t.pos.withStart(offset)) :: rest - case nil => nil - } - } - - /** ImportExpr ::= StableId `.' (Id | `_' | ImportSelectors) - */ - val importExpr = () => path(thisOK = false, handleImport) match { - case imp: Import => - imp - case sel @ Select(qual, name) => - val selector = atPos(sel.pos.point) { Ident(name) } - cpy.Import(sel)(qual, selector :: Nil) - case t => - accept(DOT) - Import(t, Ident(nme.WILDCARD) :: Nil) - } - - val handleImport = { tree: Tree => - if (in.token == USCORE) Import(tree, importSelector() :: Nil) - else if (in.token == LBRACE) Import(tree, inBraces(importSelectors())) - else tree - } - - /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}' - */ - def importSelectors(): List[Tree] = - if (in.token == RBRACE) Nil - else { - val sel = importSelector() - sel :: { - if (!isWildcardArg(sel) && in.token == COMMA) { - in.nextToken() - importSelectors() - } - else Nil - } - } - - /** ImportSelector ::= Id [`=>' Id | `=>' `_'] - */ - def importSelector(): Tree = { - val from = termIdentOrWildcard() - if (from.name != nme.WILDCARD && in.token == ARROW) - atPos(from.pos.start, in.skipToken()) { - Thicket(from, termIdentOrWildcard()) - } - else from - } - - def posMods(start: Int, mods: Modifiers) = { - val mods1 = atPos(start)(mods) - in.nextToken() - mods1 - } - - /** Def ::= val PatDef - * | var VarDef - * | def DefDef - * | type {nl} TypeDcl - * | TmplDef - * Dcl ::= val ValDcl - * | var ValDcl - * | def DefDcl - * | type {nl} TypeDcl - */ - def defOrDcl(start: Int, mods: Modifiers): Tree = in.token match { - case VAL => - val mod = atPos(in.skipToken()) { Mod.Val() } - val mods1 = mods.withAddedMod(mod) - patDefOrDcl(start, mods1, in.getDocComment(start)) - case VAR => - val mod = atPos(in.skipToken()) { Mod.Var() } - val mod1 = addMod(mods, mod) - patDefOrDcl(start, mod1, in.getDocComment(start)) - case DEF => - defDefOrDcl(start, posMods(start, mods), in.getDocComment(start)) - case TYPE => - typeDefOrDcl(start, posMods(start, mods), in.getDocComment(start)) - case _ => - tmplDef(start, mods) - } - - /** PatDef ::= Pattern2 {`,' Pattern2} [`:' Type] `=' Expr - * VarDef ::= PatDef | Id {`,' Id} `:' Type `=' `_' - * ValDcl ::= Id {`,' Id} `:' Type - * VarDcl ::= Id {`,' Id} `:' Type - */ - def patDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = atPos(start, nameStart) { - val lhs = commaSeparated(pattern2) - val tpt = typedOpt() - val rhs = - if (tpt.isEmpty || in.token == EQUALS) { - accept(EQUALS) - if (in.token == USCORE && !tpt.isEmpty && (mods is Mutable) && - (lhs.toList forall (_.isInstanceOf[Ident]))) { - wildcardIdent() - } else { - expr() - } - } else EmptyTree - lhs match { - case (id @ Ident(name: TermName)) :: Nil => { - ValDef(name, tpt, rhs).withMods(mods).setComment(docstring) - } case _ => - PatDef(mods, lhs, tpt, rhs) - } - } - - /** DefDef ::= DefSig (`:' Type [`=' Expr] | "=" Expr) - * | this ParamClause ParamClauses `=' ConstrExpr - * DefDcl ::= DefSig `:' Type - * DefSig ::= id [DefTypeParamClause] ParamClauses - */ - def defDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = atPos(start, nameStart) { - def scala2ProcedureSyntax(resultTypeStr: String) = { - val toInsert = - if (in.token == LBRACE) s"$resultTypeStr =" - else ": Unit " // trailing space ensures that `def f()def g()` works. - in.testScala2Mode(s"Procedure syntax no longer supported; `$toInsert' should be inserted here") && { - patch(source, Position(in.lastOffset), toInsert) - true - } - } - if (in.token == THIS) { - in.nextToken() - val vparamss = paramClauses(nme.CONSTRUCTOR) - val rhs = { - if (!(in.token == LBRACE && scala2ProcedureSyntax(""))) accept(EQUALS) - atPos(in.offset) { constrExpr() } - } - makeConstructor(Nil, vparamss, rhs).withMods(mods) - } else { - val mods1 = addFlag(mods, Method) - val name = ident() - val tparams = typeParamClauseOpt(ParamOwner.Def) - val vparamss = paramClauses(name) - var tpt = fromWithinReturnType(typedOpt()) - val rhs = - if (in.token == EQUALS) { - in.nextToken() - expr - } - else if (!tpt.isEmpty) - EmptyTree - else if (scala2ProcedureSyntax(": Unit")) { - tpt = scalaUnit - if (in.token == LBRACE) expr() - else EmptyTree - } - else { - if (!isExprIntro) syntaxError(MissingReturnType(), in.lastOffset) - accept(EQUALS) - expr() - } - DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods1).setComment(docstring) - } - } - - /** ConstrExpr ::= SelfInvocation - * | ConstrBlock - */ - def constrExpr(): Tree = - if (in.token == LBRACE) constrBlock() - else Block(selfInvocation() :: Nil, Literal(Constant(()))) - - /** SelfInvocation ::= this ArgumentExprs {ArgumentExprs} - */ - def selfInvocation(): Tree = - atPos(accept(THIS)) { - newLineOptWhenFollowedBy(LBRACE) - argumentExprss(Apply(Ident(nme.CONSTRUCTOR), argumentExprs())) - } - - /** ConstrBlock ::= `{' SelfInvocation {semi BlockStat} `}' - */ - def constrBlock(): Tree = - atPos(in.skipToken()) { - val stats = selfInvocation() :: { - if (isStatSep) { in.nextToken(); blockStatSeq() } - else Nil - } - accept(RBRACE) - Block(stats, Literal(Constant(()))) - } - - /** TypeDef ::= type Id [TypeParamClause] `=' Type - * TypeDcl ::= type Id [TypeParamClause] TypeBounds - */ - def typeDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = { - newLinesOpt() - atPos(start, nameStart) { - val name = ident().toTypeName - val tparams = typeParamClauseOpt(ParamOwner.Type) - in.token match { - case EQUALS => - in.nextToken() - TypeDef(name, lambdaAbstract(tparams, typ())).withMods(mods).setComment(docstring) - case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | EOF => - TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(docstring) - case _ => - syntaxErrorOrIncomplete("`=', `>:', or `<:' expected") - EmptyTree - } - } - } - - /** TmplDef ::= ([`case'] `class' | `trait') ClassDef - * | [`case'] `object' ObjectDef - */ - def tmplDef(start: Int, mods: Modifiers): Tree = { - val docstring = in.getDocComment(start) - in.token match { - case TRAIT => - classDef(start, posMods(start, addFlag(mods, Trait)), docstring) - case CLASS => - classDef(start, posMods(start, mods), docstring) - case CASECLASS => - classDef(start, posMods(start, mods | Case), docstring) - case OBJECT => - objectDef(start, posMods(start, mods | Module), docstring) - case CASEOBJECT => - objectDef(start, posMods(start, mods | Case | Module), docstring) - case _ => - syntaxErrorOrIncomplete("expected start of definition") - EmptyTree - } - } - - /** ClassDef ::= Id [ClsTypeParamClause] - * [ConstrMods] ClsParamClauses TemplateOpt - */ - def classDef(start: Offset, mods: Modifiers, docstring: Option[Comment]): TypeDef = atPos(start, nameStart) { - val name = ident().toTypeName - val constr = atPos(in.lastOffset) { - val tparams = typeParamClauseOpt(ParamOwner.Class) - val cmods = constrModsOpt() - val vparamss = paramClauses(name, mods is Case) - - makeConstructor(tparams, vparamss).withMods(cmods) - } - val templ = templateOpt(constr) - - TypeDef(name, templ).withMods(mods).setComment(docstring) - } - - /** ConstrMods ::= AccessModifier - * | Annotation {Annotation} (AccessModifier | `this') - */ - def constrModsOpt(): Modifiers = { - val mods = modifiers(accessModifierTokens, annotsAsMods()) - if (mods.hasAnnotations && !mods.hasFlags) - if (in.token == THIS) in.nextToken() - else syntaxError("`private', `protected', or `this' expected") - mods - } - - /** ObjectDef ::= Id TemplateOpt - */ - def objectDef(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): ModuleDef = atPos(start, nameStart) { - val name = ident() - val template = templateOpt(emptyConstructor) - - ModuleDef(name, template).withMods(mods).setComment(docstring) - } - -/* -------- TEMPLATES ------------------------------------------- */ - - /** ConstrApp ::= SimpleType {ParArgumentExprs} - */ - val constrApp = () => { - val t = annotType() - if (in.token == LPAREN) parArgumentExprss(wrapNew(t)) - else t - } - - /** Template ::= ConstrApps [TemplateBody] | TemplateBody - * ConstrApps ::= ConstrApp {`with' ConstrApp} - * - * @return a pair consisting of the template, and a boolean which indicates - * whether the template misses a body (i.e. no {...} part). - */ - def template(constr: DefDef): (Template, Boolean) = { - newLineOptWhenFollowedBy(LBRACE) - if (in.token == LBRACE) (templateBodyOpt(constr, Nil), false) - else { - val parents = tokenSeparated(WITH, constrApp) - newLineOptWhenFollowedBy(LBRACE) - val missingBody = in.token != LBRACE - (templateBodyOpt(constr, parents), missingBody) - } - } - - /** TemplateOpt = [`extends' Template | TemplateBody] - */ - def templateOpt(constr: DefDef): Template = - if (in.token == EXTENDS) { in.nextToken(); template(constr)._1 } - else { - newLineOptWhenFollowedBy(LBRACE) - if (in.token == LBRACE) template(constr)._1 - else Template(constr, Nil, EmptyValDef, Nil) - } - - /** TemplateBody ::= [nl] `{' TemplateStatSeq `}' - */ - def templateBodyOpt(constr: DefDef, parents: List[Tree]) = { - val (self, stats) = - if (in.token == LBRACE) templateBody() else (EmptyValDef, Nil) - Template(constr, parents, self, stats) - } - - def templateBody(): (ValDef, List[Tree]) = { - val r = inDefScopeBraces { templateStatSeq() } - if (in.token == WITH) { - syntaxError(EarlyDefinitionsNotSupported()) - in.nextToken() - template(emptyConstructor) - } - r - } - -/* -------- STATSEQS ------------------------------------------- */ - - /** Create a tree representing a packaging */ - def makePackaging(start: Int, pkg: Tree, stats: List[Tree]): PackageDef = pkg match { - case x: RefTree => atPos(start, pkg.pos.point)(PackageDef(x, stats)) - } - - /** Packaging ::= package QualId [nl] `{' TopStatSeq `}' - */ - def packaging(start: Int): Tree = { - val pkg = qualId() - newLineOptWhenFollowedBy(LBRACE) - val stats = inDefScopeBraces(topStatSeq) - makePackaging(start, pkg, stats) - } - - /** TopStatSeq ::= TopStat {semi TopStat} - * TopStat ::= Annotations Modifiers TmplDef - * | Packaging - * | package object objectDef - * | Import - * | - */ - def topStatSeq(): List[Tree] = { - val stats = new ListBuffer[Tree] - while (!isStatSeqEnd) { - setLastStatOffset() - if (in.token == PACKAGE) { - val start = in.skipToken() - if (in.token == OBJECT) - stats += objectDef(start, atPos(start, in.skipToken()) { Modifiers(Package) }) - else stats += packaging(start) - } - else if (in.token == IMPORT) - stats ++= importClause() - else if (in.token == AT || isTemplateIntro || isModifier) - stats += tmplDef(in.offset, defAnnotsMods(modifierTokens)) - else if (!isStatSep) { - if (in.token == CASE) - syntaxErrorOrIncomplete("only `case class` or `case object` allowed") - else - syntaxErrorOrIncomplete("expected class or object definition") - if (mustStartStat) // do parse all definitions even if they are probably local (i.e. a "}" has been forgotten) - defOrDcl(in.offset, defAnnotsMods(modifierTokens)) - } - acceptStatSepUnlessAtEnd() - } - stats.toList - } - - /** TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat} - * TemplateStat ::= Import - * | Annotations Modifiers Def - * | Annotations Modifiers Dcl - * | Expr1 - * | super ArgumentExprs {ArgumentExprs} - * | - */ - def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders { - var self: ValDef = EmptyValDef - val stats = new ListBuffer[Tree] - if (isExprIntro) { - val first = expr1() - if (in.token == ARROW) { - first match { - case Typed(tree @ This(EmptyTypeIdent), tpt) => - self = makeSelfDef(nme.WILDCARD, tpt).withPos(first.pos) - case _ => - val ValDef(name, tpt, _) = convertToParam(first, expected = "self type clause") - if (name != nme.ERROR) - self = makeSelfDef(name, tpt).withPos(first.pos) - } - in.nextToken() - } else { - stats += first - acceptStatSepUnlessAtEnd() - } - } - var exitOnError = false - while (!isStatSeqEnd && !exitOnError) { - setLastStatOffset() - if (in.token == IMPORT) - stats ++= importClause() - else if (isExprIntro) - stats += expr1() - else if (isDefIntro(modifierTokens)) - stats += defOrDcl(in.offset, defAnnotsMods(modifierTokens)) - else if (!isStatSep) { - exitOnError = mustStartStat - syntaxErrorOrIncomplete("illegal start of definition") - } - acceptStatSepUnlessAtEnd() - } - (self, if (stats.isEmpty) List(EmptyTree) else stats.toList) - } - - /** RefineStatSeq ::= RefineStat {semi RefineStat} - * RefineStat ::= Dcl - * | - * (in reality we admit Defs and filter them out afterwards) - */ - def refineStatSeq(): List[Tree] = { - val stats = new ListBuffer[Tree] - while (!isStatSeqEnd) { - if (isDclIntro) { - stats += defOrDcl(in.offset, Modifiers()) - } else if (!isStatSep) { - syntaxErrorOrIncomplete( - "illegal start of declaration" + - (if (inFunReturnType) " (possible cause: missing `=' in front of current method body)" - else "")) - } - acceptStatSepUnlessAtEnd() - } - stats.toList - } - - def localDef(start: Int, implicitFlag: FlagSet, implicitMod: Option[Mod] = None): Tree = { - var mods = addFlag(defAnnotsMods(localModifierTokens), implicitFlag) - if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get) - defOrDcl(start, mods) - } - - /** BlockStatSeq ::= { BlockStat semi } [ResultExpr] - * BlockStat ::= Import - * | Annotations [implicit] [lazy] Def - * | Annotations LocalModifiers TmplDef - * | Expr1 - * | - */ - def blockStatSeq(): List[Tree] = checkNoEscapingPlaceholders { - val stats = new ListBuffer[Tree] - var exitOnError = false - while (!isStatSeqEnd && in.token != CASE && !exitOnError) { - setLastStatOffset() - if (in.token == IMPORT) - stats ++= importClause() - else if (isExprIntro) - stats += expr(Location.InBlock) - else if (isDefIntro(localModifierTokens)) - if (in.token == IMPLICIT) { - val start = in.offset - val mod = atPos(in.skipToken()) { Mod.Implicit(ImplicitCommon) } - if (isIdent) stats += implicitClosure(start, Location.InBlock, Some(mod)) - else stats += localDef(start, ImplicitCommon, Some(mod)) - } else { - stats += localDef(in.offset, EmptyFlags) - } - else if (!isStatSep && (in.token != CASE)) { - exitOnError = mustStartStat - val addendum = if (isModifier) " (no modifiers allowed here)" else "" - syntaxErrorOrIncomplete("illegal start of statement" + addendum) - } - acceptStatSepUnlessAtEnd(CASE) - } - stats.toList - } - - /** CompilationUnit ::= {package QualId semi} TopStatSeq - */ - def compilationUnit(): Tree = checkNoEscapingPlaceholders { - def topstats(): List[Tree] = { - val ts = new ListBuffer[Tree] - while (in.token == SEMI) in.nextToken() - val start = in.offset - if (in.token == PACKAGE) { - in.nextToken() - if (in.token == OBJECT) { - val docstring = in.getDocComment(start) - ts += objectDef(start, atPos(start, in.skipToken()) { Modifiers(Package) }, docstring) - if (in.token != EOF) { - acceptStatSep() - ts ++= topStatSeq() - } - } else { - val pkg = qualId() - newLineOptWhenFollowedBy(LBRACE) - if (in.token == EOF) - ts += makePackaging(start, pkg, List()) - else if (in.token == LBRACE) { - ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq())) - acceptStatSepUnlessAtEnd() - ts ++= topStatSeq() - } - else { - acceptStatSep() - ts += makePackaging(start, pkg, topstats()) - } - } - } - else - ts ++= topStatSeq() - - ts.toList - } - - topstats() match { - case List(stat @ PackageDef(_, _)) => stat - case Nil => EmptyTree // without this case we'd get package defs without positions - case stats => PackageDef(Ident(nme.EMPTY_PACKAGE), stats) - } - } - } - - - class OutlineParser(source: SourceFile)(implicit ctx: Context) extends Parser(source) { - - def skipBraces[T](body: T): T = { - accept(LBRACE) - var openBraces = 1 - while (in.token != EOF && openBraces > 0) { - if (in.token == XMLSTART) xmlLiteral() - else { - if (in.token == LBRACE) openBraces += 1 - else if (in.token == RBRACE) openBraces -= 1 - in.nextToken() - } - } - body - } - - override def blockExpr(): Tree = skipBraces(EmptyTree) - - override def templateBody() = skipBraces((EmptyValDef, List(EmptyTree))) - } -} diff --git a/src/dotty/tools/dotc/parsing/Scanners.scala b/src/dotty/tools/dotc/parsing/Scanners.scala deleted file mode 100644 index 60003d098..000000000 --- a/src/dotty/tools/dotc/parsing/Scanners.scala +++ /dev/null @@ -1,1014 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import core.Names._, core.Contexts._, core.Decorators._, util.Positions._ -import core.StdNames._, core.Comments._ -import util.SourceFile -import java.lang.Character.isDigit -import scala.reflect.internal.Chars._ -import Tokens._ -import scala.annotation.{ switch, tailrec } -import scala.collection.mutable -import mutable.ListBuffer -import Utility.isNameStart -import rewrite.Rewrites.patch - -object Scanners { - - /** Offset into source character array */ - type Offset = Int - - /** An undefined offset */ - val NoOffset: Offset = -1 - - type Token = Int - - trait TokenData { - - /** the next token */ - var token: Token = EMPTY - - /** the offset of the first character of the current token */ - var offset: Offset = 0 - - /** the offset of the character following the token preceding this one */ - var lastOffset: Offset = 0 - - /** the name of an identifier */ - var name: TermName = null - - /** the string value of a literal */ - var strVal: String = null - - /** the base of a number */ - var base: Int = 0 - - def copyFrom(td: TokenData) = { - this.token = td.token - this.offset = td.offset - this.lastOffset = td.lastOffset - this.name = td.name - this.strVal = td.strVal - this.base = td.base - } - } - - abstract class ScannerCommon(source: SourceFile)(implicit ctx: Context) extends CharArrayReader with TokenData { - val buf = source.content - - // Errors ----------------------------------------------------------------- - - /** the last error offset - */ - var errOffset: Offset = NoOffset - - - /** Generate an error at the given offset */ - def error(msg: String, off: Offset = offset) = { - ctx.error(msg, source atPos Position(off)) - token = ERROR - errOffset = off - } - - /** signal an error where the input ended in the middle of a token */ - def incompleteInputError(msg: String): Unit = { - ctx.incompleteInputError(msg, source atPos Position(offset)) - token = EOF - errOffset = offset - } - - // Setting token data ---------------------------------------------------- - - /** A character buffer for literals - */ - val litBuf = new StringBuilder - - /** append Unicode character to "litBuf" buffer - */ - protected def putChar(c: Char): Unit = litBuf.append(c) - - /** Return buffer contents and clear */ - def flushBuf(buf: StringBuilder): String = { - val str = buf.toString - buf.clear() - str - } - - /** Clear buffer and set name and token */ - def finishNamed(idtoken: Token = IDENTIFIER, target: TokenData = this): Unit = { - target.name = flushBuf(litBuf).toTermName - target.token = idtoken - if (idtoken == IDENTIFIER) { - val idx = target.name.start - target.token = toToken(idx) - } - } - - def toToken(idx: Int): Token - - /** Clear buffer and set string */ - def setStrVal() = - strVal = flushBuf(litBuf) - - /** Convert current strVal to char value - */ - def charVal: Char = if (strVal.length > 0) strVal.charAt(0) else 0 - - /** Convert current strVal, base to long value - * This is tricky because of max negative value. - */ - def intVal(negated: Boolean): Long = { - if (token == CHARLIT && !negated) { - charVal - } else { - var value: Long = 0 - val divider = if (base == 10) 1 else 2 - val limit: Long = - if (token == LONGLIT) Long.MaxValue else Int.MaxValue - var i = 0 - val len = strVal.length - while (i < len) { - val d = digit2int(strVal charAt i, base) - if (d < 0) { - error("malformed integer number") - return 0 - } - if (value < 0 || - limit / (base / divider) < value || - limit - (d / divider) < value * (base / divider) && - !(negated && limit == value * base - 1 + d)) { - error("integer number too large") - return 0 - } - value = value * base + d - i += 1 - } - if (negated) -value else value - } - } - - def intVal: Long = intVal(false) - - /** Convert current strVal, base to double value - */ - def floatVal(negated: Boolean): Double = { - val limit: Double = - if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue - try { - val value: Double = java.lang.Double.valueOf(strVal).doubleValue() - if (value > limit) - error("floating point number too large") - if (negated) -value else value - } catch { - case _: NumberFormatException => - error("malformed floating point number") - 0.0 - } - } - - def floatVal: Double = floatVal(false) - - } - - class Scanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) { - val keepComments = ctx.settings.YkeepComments.value - - /** All doc comments as encountered, each list contains doc comments from - * the same block level. Starting with the deepest level and going upward - */ - private[this] var docsPerBlockStack: List[List[Comment]] = List(Nil) - - /** Adds level of nesting to docstrings */ - def enterBlock(): Unit = - docsPerBlockStack = List(Nil) ::: docsPerBlockStack - - /** Removes level of nesting for docstrings */ - def exitBlock(): Unit = docsPerBlockStack = docsPerBlockStack match { - case x :: Nil => List(Nil) - case _ => docsPerBlockStack.tail - } - - /** Returns the closest docstring preceding the position supplied */ - def getDocComment(pos: Int): Option[Comment] = { - def closest(c: Comment, docstrings: List[Comment]): Comment = docstrings match { - case x :: xs if (c.pos.end < x.pos.end && x.pos.end <= pos) => closest(x, xs) - case Nil => c - } - - docsPerBlockStack match { - case (list @ (x :: xs)) :: _ => { - val c = closest(x, xs) - docsPerBlockStack = list.dropWhile(_ != c).tail :: docsPerBlockStack.tail - Some(c) - } - case _ => None - } - } - - /** A buffer for comments */ - val commentBuf = new StringBuilder - - private def handleMigration(keyword: Token): Token = - if (!isScala2Mode) keyword - else if (keyword == INLINE) treatAsIdent() - else keyword - - - private def treatAsIdent() = { - testScala2Mode(i"$name is now a keyword, write `$name` instead of $name to keep it as an identifier") - patch(source, Position(offset), "`") - patch(source, Position(offset + name.length), "`") - IDENTIFIER - } - - def toToken(idx: Int): Token = - if (idx >= 0 && idx <= lastKeywordStart) handleMigration(kwArray(idx)) - else IDENTIFIER - - private class TokenData0 extends TokenData - - /** we need one token lookahead and one token history - */ - val next : TokenData = new TokenData0 - private val prev : TokenData = new TokenData0 - - /** a stack of tokens which indicates whether line-ends can be statement separators - * also used for keeping track of nesting levels. - * We keep track of the closing symbol of a region. This can be - * RPAREN if region starts with '(' - * RBRACKET if region starts with '[' - * RBRACE if region starts with '{' - * ARROW if region starts with `case' - * STRINGLIT if region is a string interpolation expression starting with '${' - * (the STRINGLIT appears twice in succession on the stack iff the - * expression is a multiline string literal). - */ - var sepRegions: List[Token] = List() - -// Scala 2 compatibility - - val isScala2Mode = ctx.settings.language.value.contains(nme.Scala2.toString) - - /** Cannot use ctx.featureEnabled because accessing the context would force too much */ - def testScala2Mode(msg: String, pos: Position = Position(offset)) = { - if (isScala2Mode) ctx.migrationWarning(msg, source atPos pos) - isScala2Mode - } - -// Get next token ------------------------------------------------------------ - - /** Are we directly in a string interpolation expression? - */ - private def inStringInterpolation = - sepRegions.nonEmpty && sepRegions.head == STRINGLIT - - /** Are we directly in a multiline string interpolation expression? - * @pre inStringInterpolation - */ - private def inMultiLineInterpolation = - inStringInterpolation && sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART - - /** read next token and return last offset - */ - def skipToken(): Offset = { - val off = offset - nextToken() - off - } - - def adjustSepRegions(lastToken: Token): Unit = (lastToken: @switch) match { - case LPAREN => - sepRegions = RPAREN :: sepRegions - case LBRACKET => - sepRegions = RBRACKET :: sepRegions - case LBRACE => - sepRegions = RBRACE :: sepRegions - case CASE => - sepRegions = ARROW :: sepRegions - case RBRACE => - while (!sepRegions.isEmpty && sepRegions.head != RBRACE) - sepRegions = sepRegions.tail - if (!sepRegions.isEmpty) sepRegions = sepRegions.tail - case RBRACKET | RPAREN => - if (!sepRegions.isEmpty && sepRegions.head == lastToken) - sepRegions = sepRegions.tail - case ARROW => - if (!sepRegions.isEmpty && sepRegions.head == lastToken) - sepRegions = sepRegions.tail - case STRINGLIT => - if (inMultiLineInterpolation) - sepRegions = sepRegions.tail.tail - else if (inStringInterpolation) - sepRegions = sepRegions.tail - case _ => - } - - /** Produce next token, filling TokenData fields of Scanner. - */ - def nextToken(): Unit = { - val lastToken = token - adjustSepRegions(lastToken) - - // Read a token or copy it from `next` tokenData - if (next.token == EMPTY) { - lastOffset = lastCharOffset - if (inStringInterpolation) fetchStringPart() - else fetchToken() - if (token == ERROR) adjustSepRegions(STRINGLIT) - } else { - this copyFrom next - next.token = EMPTY - } - - /** Insert NEWLINE or NEWLINES if - * - we are after a newline - * - we are within a { ... } or on toplevel (wrt sepRegions) - * - the current token can start a statement and the one before can end it - * insert NEWLINES if we are past a blank line, NEWLINE otherwise - */ - if (isAfterLineEnd() && - (canEndStatTokens contains lastToken) && - (canStartStatTokens contains token) && - (sepRegions.isEmpty || sepRegions.head == RBRACE)) { - next copyFrom this - // todo: make offset line-end of previous line? - offset = if (lineStartOffset <= offset) lineStartOffset else lastLineStartOffset - token = if (pastBlankLine()) NEWLINES else NEWLINE - } - - postProcessToken() - // print("[" + this +"]") - } - - def postProcessToken() = { - // Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE - def lookahead() = { - prev copyFrom this - fetchToken() - } - def reset(nextLastOffset: Offset) = { - lastOffset = nextLastOffset - next copyFrom this - this copyFrom prev - } - def fuse(tok: Int) = { - token = tok - offset = prev.offset - lastOffset = prev.lastOffset - } - if (token == CASE) { - val nextLastOffset = lastCharOffset - lookahead() - if (token == CLASS) fuse(CASECLASS) - else if (token == OBJECT) fuse(CASEOBJECT) - else reset(nextLastOffset) - } else if (token == SEMI) { - val nextLastOffset = lastCharOffset - lookahead() - if (token != ELSE) reset(nextLastOffset) - } - } - - /** Is current token first one after a newline? */ - def isAfterLineEnd(): Boolean = - lastOffset < lineStartOffset && - (lineStartOffset <= offset || - lastOffset < lastLineStartOffset && lastLineStartOffset <= offset) - - /** Is there a blank line between the current token and the last one? - * @pre afterLineEnd(). - */ - private def pastBlankLine(): Boolean = { - val end = offset - def recur(idx: Offset, isBlank: Boolean): Boolean = - idx < end && { - val ch = buf(idx) - if (ch == LF || ch == FF) isBlank || recur(idx + 1, true) - else recur(idx + 1, isBlank && ch <= ' ') - } - recur(lastOffset, false) - } - - /** read next token, filling TokenData fields of Scanner. - */ - protected final def fetchToken(): Unit = { - offset = charOffset - 1 - (ch: @switch) match { - case ' ' | '\t' | CR | LF | FF => - nextChar() - fetchToken() - case 'A' | 'B' | 'C' | 'D' | 'E' | - 'F' | 'G' | 'H' | 'I' | 'J' | - 'K' | 'L' | 'M' | 'N' | 'O' | - 'P' | 'Q' | 'R' | 'S' | 'T' | - 'U' | 'V' | 'W' | 'X' | 'Y' | - 'Z' | '$' | '_' | - 'a' | 'b' | 'c' | 'd' | 'e' | - 'f' | 'g' | 'h' | 'i' | 'j' | - 'k' | 'l' | 'm' | 'n' | 'o' | - 'p' | 'q' | 'r' | 's' | 't' | - 'u' | 'v' | 'w' | 'x' | 'y' | - 'z' => - putChar(ch) - nextChar() - getIdentRest() - if (ch == '"' && token == IDENTIFIER) - token = INTERPOLATIONID - case '<' => // is XMLSTART? - def fetchLT() = { - val last = if (charOffset >= 2) buf(charOffset - 2) else ' ' - nextChar() - last match { - case ' ' | '\t' | '\n' | '{' | '(' | '>' if isNameStart(ch) || ch == '!' || ch == '?' => - token = XMLSTART - case _ => - // Console.println("found '<', but last is '" + in.last +"'"); // DEBUG - putChar('<') - getOperatorRest() - } - } - fetchLT - case '~' | '!' | '@' | '#' | '%' | - '^' | '*' | '+' | '-' | /*'<' | */ - '>' | '?' | ':' | '=' | '&' | - '|' | '\\' => - putChar(ch) - nextChar() - getOperatorRest() - case '/' => - if (skipComment()) { - fetchToken() - } else { - putChar('/') - getOperatorRest() - } - case '0' => - def fetchZero() = { - putChar(ch) - nextChar() - if (ch == 'x' || ch == 'X') { - nextChar() - base = 16 - } else { - /** - * What should leading 0 be in the future? It is potentially dangerous - * to let it be base-10 because of history. Should it be an error? Is - * there a realistic situation where one would need it? - */ - if (isDigit(ch)) - error("Non-zero numbers may not have a leading zero.") - base = 10 - } - getNumber() - } - fetchZero - case '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => - base = 10 - getNumber() - case '`' => - getBackquotedIdent() - case '\"' => - def fetchDoubleQuote() = { - if (token == INTERPOLATIONID) { - nextRawChar() - if (ch == '\"') { - nextRawChar() - if (ch == '\"') { - nextRawChar() - getStringPart(multiLine = true) - sepRegions = STRINGPART :: sepRegions // indicate string part - sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part - } else { - token = STRINGLIT - strVal = "" - } - } else { - getStringPart(multiLine = false) - sepRegions = STRINGLIT :: sepRegions // indicate single line string part - } - } else { - nextChar() - if (ch == '\"') { - nextChar() - if (ch == '\"') { - nextRawChar() - getRawStringLit() - } else { - token = STRINGLIT - strVal = "" - } - } else { - getStringLit() - } - } - } - fetchDoubleQuote - case '\'' => - def fetchSingleQuote() = { - nextChar() - if (isIdentifierStart(ch)) - charLitOr(getIdentRest) - else if (isOperatorPart(ch) && (ch != '\\')) - charLitOr(getOperatorRest) - else { - getLitChar() - if (ch == '\'') { - nextChar() - token = CHARLIT - setStrVal() - } else { - error("unclosed character literal") - } - } - } - fetchSingleQuote - case '.' => - nextChar() - if ('0' <= ch && ch <= '9') { - putChar('.'); getFraction(); setStrVal() - } else { - token = DOT - } - case ';' => - nextChar(); token = SEMI - case ',' => - nextChar(); token = COMMA - case '(' => - enterBlock(); nextChar(); token = LPAREN - case '{' => - enterBlock(); nextChar(); token = LBRACE - case ')' => - exitBlock(); nextChar(); token = RPAREN - case '}' => - exitBlock(); nextChar(); token = RBRACE - case '[' => - nextChar(); token = LBRACKET - case ']' => - nextChar(); token = RBRACKET - case SU => - if (isAtEnd) token = EOF - else { - error("illegal character") - nextChar() - } - case _ => - def fetchOther() = { - if (ch == '\u21D2') { - nextChar(); token = ARROW - } else if (ch == '\u2190') { - nextChar(); token = LARROW - } else if (Character.isUnicodeIdentifierStart(ch)) { - putChar(ch) - nextChar() - getIdentRest() - } else if (isSpecial(ch)) { - putChar(ch) - nextChar() - getOperatorRest() - } else { - error(f"illegal character '\\u${ch: Int}%04x'") - nextChar() - } - } - fetchOther - } - } - - private def skipComment(): Boolean = { - def appendToComment(ch: Char) = - if (keepComments) commentBuf.append(ch) - def nextChar() = { - appendToComment(ch) - Scanner.this.nextChar() - } - def skipLine(): Unit = { - nextChar() - if ((ch != CR) && (ch != LF) && (ch != SU)) skipLine() - } - @tailrec - def skipComment(): Unit = { - if (ch == '/') { - nextChar() - if (ch == '*') nestedComment() - skipComment() - } - else if (ch == '*') { - do nextChar() while (ch == '*') - if (ch == '/') nextChar() - else skipComment() - } - else if (ch == SU) incompleteInputError("unclosed comment") - else { nextChar(); skipComment() } - } - def nestedComment() = { nextChar(); skipComment() } - val start = lastCharOffset - def finishComment(): Boolean = { - if (keepComments) { - val pos = Position(start, charOffset, start) - val comment = Comment(pos, flushBuf(commentBuf)) - - if (comment.isDocComment) - docsPerBlockStack = (docsPerBlockStack.head :+ comment) :: docsPerBlockStack.tail - } - - true - } - nextChar() - if (ch == '/') { skipLine(); finishComment() } - else if (ch == '*') { nextChar(); skipComment(); finishComment() } - else false - } - -// Identifiers --------------------------------------------------------------- - - private def getBackquotedIdent(): Unit = { - nextChar() - getLitChars('`') - if (ch == '`') { - nextChar() - finishNamed(BACKQUOTED_IDENT) - if (name.length == 0) - error("empty quoted identifier") - else if (name == nme.WILDCARD) - error("wildcard invalid as backquoted identifier") - } - else error("unclosed quoted identifier") - } - - private def getIdentRest(): Unit = (ch: @switch) match { - case 'A' | 'B' | 'C' | 'D' | 'E' | - 'F' | 'G' | 'H' | 'I' | 'J' | - 'K' | 'L' | 'M' | 'N' | 'O' | - 'P' | 'Q' | 'R' | 'S' | 'T' | - 'U' | 'V' | 'W' | 'X' | 'Y' | - 'Z' | '$' | - 'a' | 'b' | 'c' | 'd' | 'e' | - 'f' | 'g' | 'h' | 'i' | 'j' | - 'k' | 'l' | 'm' | 'n' | 'o' | - 'p' | 'q' | 'r' | 's' | 't' | - 'u' | 'v' | 'w' | 'x' | 'y' | - 'z' | - '0' | '1' | '2' | '3' | '4' | - '5' | '6' | '7' | '8' | '9' => - putChar(ch) - nextChar() - getIdentRest() - case '_' => - putChar(ch) - nextChar() - getIdentOrOperatorRest() - case SU => // strangely enough, Character.isUnicodeIdentifierPart(SU) returns true! - finishNamed() - case _ => - if (Character.isUnicodeIdentifierPart(ch)) { - putChar(ch) - nextChar() - getIdentRest() - } else { - finishNamed() - } - } - - private def getOperatorRest(): Unit = (ch: @switch) match { - case '~' | '!' | '@' | '#' | '%' | - '^' | '*' | '+' | '-' | '<' | - '>' | '?' | ':' | '=' | '&' | - '|' | '\\' => - putChar(ch); nextChar(); getOperatorRest() - case '/' => - if (skipComment()) finishNamed() - else { putChar('/'); getOperatorRest() } - case _ => - if (isSpecial(ch)) { putChar(ch); nextChar(); getOperatorRest() } - else finishNamed() - } - - private def getIdentOrOperatorRest(): Unit = { - if (isIdentifierPart(ch)) - getIdentRest() - else ch match { - case '~' | '!' | '@' | '#' | '%' | - '^' | '*' | '+' | '-' | '<' | - '>' | '?' | ':' | '=' | '&' | - '|' | '\\' | '/' => - getOperatorRest() - case _ => - if (isSpecial(ch)) getOperatorRest() - else finishNamed() - } - } - - -// Literals ----------------------------------------------------------------- - - private def getStringLit() = { - getLitChars('"') - if (ch == '"') { - setStrVal() - nextChar() - token = STRINGLIT - } else error("unclosed string literal") - } - - private def getRawStringLit(): Unit = { - if (ch == '\"') { - nextRawChar() - if (isTripleQuote()) { - setStrVal() - token = STRINGLIT - } else - getRawStringLit() - } else if (ch == SU) { - incompleteInputError("unclosed multi-line string literal") - } else { - putChar(ch) - nextRawChar() - getRawStringLit() - } - } - - @annotation.tailrec private def getStringPart(multiLine: Boolean): Unit = { - def finishStringPart() = { - setStrVal() - token = STRINGPART - next.lastOffset = charOffset - 1 - next.offset = charOffset - 1 - } - if (ch == '"') { - if (multiLine) { - nextRawChar() - if (isTripleQuote()) { - setStrVal() - token = STRINGLIT - } else - getStringPart(multiLine) - } else { - nextChar() - setStrVal() - token = STRINGLIT - } - } else if (ch == '$') { - nextRawChar() - if (ch == '$') { - putChar(ch) - nextRawChar() - getStringPart(multiLine) - } else if (ch == '{') { - finishStringPart() - nextRawChar() - next.token = LBRACE - } else if (Character.isUnicodeIdentifierStart(ch)) { - finishStringPart() - do { - putChar(ch) - nextRawChar() - } while (ch != SU && Character.isUnicodeIdentifierPart(ch)) - finishNamed(target = next) - } else { - error("invalid string interpolation: `$$', `$'ident or `$'BlockExpr expected") - } - } else { - val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF))) - if (isUnclosedLiteral) { - if (multiLine) - incompleteInputError("unclosed multi-line string literal") - else - error("unclosed string literal") - } - else { - putChar(ch) - nextRawChar() - getStringPart(multiLine) - } - } - } - - private def fetchStringPart() = { - offset = charOffset - 1 - getStringPart(multiLine = inMultiLineInterpolation) - } - - private def isTripleQuote(): Boolean = - if (ch == '"') { - nextRawChar() - if (ch == '"') { - nextChar() - while (ch == '"') { - putChar('"') - nextChar() - } - true - } else { - putChar('"') - putChar('"') - false - } - } else { - putChar('"') - false - } - - /** copy current character into litBuf, interpreting any escape sequences, - * and advance to next character. - */ - protected def getLitChar(): Unit = - if (ch == '\\') { - nextChar() - if ('0' <= ch && ch <= '7') { - val leadch: Char = ch - var oct: Int = digit2int(ch, 8) - nextChar() - if ('0' <= ch && ch <= '7') { - oct = oct * 8 + digit2int(ch, 8) - nextChar() - if (leadch <= '3' && '0' <= ch && ch <= '7') { - oct = oct * 8 + digit2int(ch, 8) - nextChar() - } - } - putChar(oct.toChar) - } else { - ch match { - case 'b' => putChar('\b') - case 't' => putChar('\t') - case 'n' => putChar('\n') - case 'f' => putChar('\f') - case 'r' => putChar('\r') - case '\"' => putChar('\"') - case '\'' => putChar('\'') - case '\\' => putChar('\\') - case _ => invalidEscape() - } - nextChar() - } - } else { - putChar(ch) - nextChar() - } - - protected def invalidEscape(): Unit = { - error("invalid escape character", charOffset - 1) - putChar(ch) - } - - private def getLitChars(delimiter: Char) = { - while (ch != delimiter && !isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape)) - getLitChar() - } - - /** read fractional part and exponent of floating point number - * if one is present. - */ - protected def getFraction(): Unit = { - token = DOUBLELIT - while ('0' <= ch && ch <= '9') { - putChar(ch) - nextChar() - } - if (ch == 'e' || ch == 'E') { - val lookahead = lookaheadReader - lookahead.nextChar() - if (lookahead.ch == '+' || lookahead.ch == '-') { - lookahead.nextChar() - } - if ('0' <= lookahead.ch && lookahead.ch <= '9') { - putChar(ch) - nextChar() - if (ch == '+' || ch == '-') { - putChar(ch) - nextChar() - } - while ('0' <= ch && ch <= '9') { - putChar(ch) - nextChar() - } - } - token = DOUBLELIT - } - if (ch == 'd' || ch == 'D') { - putChar(ch) - nextChar() - token = DOUBLELIT - } else if (ch == 'f' || ch == 'F') { - putChar(ch) - nextChar() - token = FLOATLIT - } - checkNoLetter() - } - def checkNoLetter(): Unit = { - if (isIdentifierPart(ch) && ch >= ' ') - error("Invalid literal number") - } - - /** Read a number into strVal and set base - */ - protected def getNumber(): Unit = { - while (digit2int(ch, base) >= 0) { - putChar(ch) - nextChar() - } - token = INTLIT - if (base == 10 && ch == '.') { - val isDefinitelyNumber = { - val lookahead = lookaheadReader - val c = lookahead.getc() - (c: @switch) match { - /** Another digit is a giveaway. */ - case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => - true - - /** Backquoted idents like 22.`foo`. */ - case '`' => - false - - /** These letters may be part of a literal, or a method invocation on an Int. - */ - case 'd' | 'D' | 'f' | 'F' => - !isIdentifierPart(lookahead.getc()) - - /** A little more special handling for e.g. 5e7 */ - case 'e' | 'E' => - val ch = lookahead.getc() - !isIdentifierPart(ch) || (isDigit(ch) || ch == '+' || ch == '-') - - case x => - !isIdentifierStart(x) - } - } - if (isDefinitelyNumber) { - putChar(ch) - nextChar() - getFraction() - } - } else (ch: @switch) match { - case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' => - if (base == 10) getFraction() - case 'l' | 'L' => - nextChar() - token = LONGLIT - case _ => - } - setStrVal() - } - - /** Parse character literal if current character is followed by \', - * or follow with given op and return a symbol literal token - */ - def charLitOr(op: () => Unit): Unit = { - putChar(ch) - nextChar() - if (ch == '\'') { - nextChar() - token = CHARLIT - setStrVal() - } else { - op() - token = SYMBOLLIT - strVal = name.toString - } - } - override def toString = - showTokenDetailed(token) + { - if ((identifierTokens contains token) || (literalTokens contains token)) " " + name - else "" - } - - def show: String = token match { - case IDENTIFIER | BACKQUOTED_IDENT => s"id($name)" - case CHARLIT => s"char($intVal)" - case INTLIT => s"int($intVal)" - case LONGLIT => s"long($intVal)" - case FLOATLIT => s"float($floatVal)" - case DOUBLELIT => s"double($floatVal)" - case STRINGLIT => s"string($strVal)" - case STRINGPART => s"stringpart($strVal)" - case INTERPOLATIONID => s"interpolationid($name)" - case SEMI => ";" - case NEWLINE => ";" - case NEWLINES => ";;" - case COMMA => "," - case _ => showToken(token) - } - -// (does not seem to be needed) def flush = { charOffset = offset; nextChar(); this } - - /* Resume normal scanning after XML */ - def resume(lastToken: Token) = { - token = lastToken - if (next.token != EMPTY && !ctx.reporter.hasErrors) - error("unexpected end of input: possible missing '}' in XML block") - - nextToken() - } - - /* Initialization: read first char, then first token */ - nextChar() - nextToken() - } // end Scanner - - // ------------- keyword configuration ----------------------------------- - - val (lastKeywordStart, kwArray) = buildKeywordArray(keywords) -} diff --git a/src/dotty/tools/dotc/parsing/ScriptParsers.scala b/src/dotty/tools/dotc/parsing/ScriptParsers.scala deleted file mode 100644 index afa7fefab..000000000 --- a/src/dotty/tools/dotc/parsing/ScriptParsers.scala +++ /dev/null @@ -1,145 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import util.SourceFile -import core._ -import Contexts._ -import Parsers._ - - -/** <p>Performs the following context-free rewritings:</p> - * <ol> - * <li> - * Places all pattern variables in Bind nodes. In a pattern, for - * identifiers <code>x</code>:<pre> - * x => x @ _ - * x:T => x @ (_ : T)</pre> - * </li> - * <li>Removes pattern definitions (PatDef's) as follows: - * If pattern is a simple (typed) identifier:<pre> - * <b>val</b> x = e ==> <b>val</b> x = e - * <b>val</b> x: T = e ==> <b>val</b> x: T = e</pre> - * - * if there are no variables in pattern<pre> - * <b>val</b> p = e ==> e match (case p => ())</pre> - * - * if there is exactly one variable in pattern<pre> - * <b>val</b> x_1 = e <b>match</b> (case p => (x_1))</pre> - * - * if there is more than one variable in pattern<pre> - * <b>val</b> p = e ==> <b>private synthetic val</b> t$ = e <b>match</b> (case p => (x_1, ..., x_N)) - * <b>val</b> x_1 = t$._1 - * ... - * <b>val</b> x_N = t$._N</pre> - * </li> - * <li> - * Removes function types as follows:<pre> - * (argtpes) => restpe ==> scala.Function_n[argtpes, restpe]</pre> - * </li> - * <li> - * Wraps naked case definitions in a match as follows:<pre> - * { cases } ==> (x => x.match {cases})<span style="font-family:normal;">, except when already argument to match</span></pre> - * </li> - * </ol> - */ -object ScriptParsers { - - import ast.untpd._ - - class ScriptParser(source: SourceFile)(implicit ctx: Context) extends Parser(source) { - - /** This is the parse entry point for code which is not self-contained, e.g. - * a script which is a series of template statements. They will be - * swaddled in Trees until the AST is equivalent to the one returned - * by compilationUnit(). - */ - override def parse(): Tree = unsupported("parse") - /* TODO: reinstantiate - val stmts = templateStatSeq(false)._2 - accept(EOF) - - def mainModuleName = ctx.settings.script.value - - /** If there is only a single object template in the file and it has a - * suitable main method, we will use it rather than building another object - * around it. Since objects are loaded lazily the whole script would have - * been a no-op, so we're not taking much liberty. - */ - def searchForMain(): Option[Tree] = { - /** Have to be fairly liberal about what constitutes a main method since - * nothing has been typed yet - for instance we can't assume the parameter - * type will look exactly like "Array[String]" as it could have been renamed - * via import, etc. - */ - def isMainMethod(t: Tree) = t match { - case DefDef(_, nme.main, Nil, List(_), _, _) => true - case _ => false - } - /** For now we require there only be one top level object. */ - var seenModule = false - val newStmts = stmts collect { - case t @ Import(_, _) => t - case md @ ModuleDef(mods, name, template) - if !seenModule && (template.body exists isMainMethod) => - seenModule = true - /** This slightly hacky situation arises because we have no way to communicate - * back to the scriptrunner what the name of the program is. Even if we were - * willing to take the sketchy route of settings.script.value = progName, that - * does not work when using fsc. And to find out in advance would impose a - * whole additional parse. So instead, if the actual object's name differs from - * what the script is expecting, we transform it to match. - */ - md.derivedModuleDef(mods, mainModuleName.toTermName, template) - case _ => - /** If we see anything but the above, fail. */ - return None - } - Some(makePackaging(0, emptyPkg, newStmts)) - } - - if (mainModuleName == ScriptRunner.defaultScriptMain) - searchForMain() foreach { return _ } - - /** Here we are building an AST representing the following source fiction, - * where <moduleName> is from -Xscript (defaults to "Main") and <stmts> are - * the result of parsing the script file. - * - * object <moduleName> { - * def main(argv: Array[String]): Unit = { - * val args = argv - * new AnyRef { - * <stmts> - * } - * } - * } - */ - import definitions._ - - def emptyPkg = atPos(0, 0, 0) { Ident(nme.EMPTY_PACKAGE_NAME) } - def emptyInit = DefDef( - Modifiers(), - nme.CONSTRUCTOR, - Nil, - List(Nil), - TypeTree(), - Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), Nil)), Literal(Constant(()))) - ) - - // def main - def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String))) - def mainParameter = List(ValDef(Modifiers(Param), "argv", mainParamType, EmptyTree)) - def mainSetArgv = List(ValDef(Modifiers(), "args", TypeTree(), Ident("argv"))) - def mainNew = makeNew(Nil, emptyValDef, stmts, List(Nil), NoPosition, NoPosition) - def mainDef = DefDef(Modifiers(), nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), Block(mainSetArgv, mainNew)) - - // object Main - def moduleName = ScriptRunner scriptMain settings - def moduleBody = Template(List(scalaScalaObjectConstr), emptyValDef, List(emptyInit, mainDef)) - def moduleDef = ModuleDef(Modifiers(), moduleName, moduleBody) - - // package <empty> { ... } - makePackaging(0, emptyPkg, List(moduleDef)) - }*/ - } -} diff --git a/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala b/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala deleted file mode 100644 index 20b655a19..000000000 --- a/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala +++ /dev/null @@ -1,264 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import scala.collection.mutable -import scala.xml.{ EntityRef, Text } -import core._ -import Flags.Mutable -import Names._, StdNames._, ast.Trees._, ast.{tpd, untpd} -import Symbols._, Contexts._ -import util.Positions._ -import Parsers.Parser -import scala.reflect.internal.util.StringOps.splitWhere -import scala.language.implicitConversions - -/** This class builds instance of `Tree` that represent XML. - * - * Note from martin: This needs to have its position info reworked. I don't - * understand exactly what's done here. To make validation pass, I set many - * positions to be transparent. Not sure this is a good idea for navigating - * XML trees in the IDE but it's the best I can do right now. If someone - * who understands this part better wants to give it a shot, please do! - * - * @author Burak Emir - * @version 1.0 - */ -class SymbolicXMLBuilder(parser: Parser, preserveWS: Boolean)(implicit ctx: Context) { - - import Constants.Constant - import untpd._ - - import parser.atPos - - private[parsing] var isPattern: Boolean = _ - - private object xmltypes extends ScalaTypeNames { - val _Comment: TypeName = "Comment" - val _Elem: TypeName = "Elem" - val _EntityRef: TypeName = "EntityRef" - val _Group: TypeName = "Group" - val _MetaData: TypeName = "MetaData" - val _NamespaceBinding: TypeName = "NamespaceBinding" - val _NodeBuffer: TypeName = "NodeBuffer" - val _PrefixedAttribute: TypeName = "PrefixedAttribute" - val _ProcInstr: TypeName = "ProcInstr" - val _Text: TypeName = "Text" - val _Unparsed: TypeName = "Unparsed" - val _UnprefixedAttribute: TypeName = "UnprefixedAttribute" - } - - private object xmlterms extends ScalaTermNames { - val _Null: TermName = "Null" - val __Elem: TermName = "Elem" - val __Text: TermName = "Text" - val _buf: TermName = "$buf" - val _md: TermName = "$md" - val _plus: TermName = "$amp$plus" - val _scope: TermName = "$scope" - val _tmpscope: TermName = "$tmpscope" - val _xml: TermName = "xml" - } - - import xmltypes.{_Comment, _Elem, _EntityRef, _Group, _MetaData, _NamespaceBinding, _NodeBuffer, - _PrefixedAttribute, _ProcInstr, _Text, _Unparsed, _UnprefixedAttribute} - - import xmlterms.{_Null, __Elem, __Text, _buf, _md, _plus, _scope, _tmpscope, _xml} - - // convenience methods - private def LL[A](x: A*): List[List[A]] = List(List(x:_*)) - private def const(x: Any) = Literal(Constant(x)) - private def wild = Ident(nme.WILDCARD) - private def wildStar = Ident(tpnme.WILDCARD_STAR) - private def _scala(name: Name) = scalaDot(name) - private def _scala_xml(name: Name) = Select(_scala(_xml), name) - - private def _scala_xml_Comment = _scala_xml(_Comment) - private def _scala_xml_Elem = _scala_xml(_Elem) - private def _scala_xml_EntityRef = _scala_xml(_EntityRef) - private def _scala_xml_Group = _scala_xml(_Group) - private def _scala_xml_MetaData = _scala_xml(_MetaData) - private def _scala_xml_NamespaceBinding = _scala_xml(_NamespaceBinding) - private def _scala_xml_NodeBuffer = _scala_xml(_NodeBuffer) - private def _scala_xml_Null = _scala_xml(_Null) - private def _scala_xml_PrefixedAttribute = _scala_xml(_PrefixedAttribute) - private def _scala_xml_ProcInstr = _scala_xml(_ProcInstr) - private def _scala_xml_Text = _scala_xml(_Text) - private def _scala_xml_Unparsed = _scala_xml(_Unparsed) - private def _scala_xml_UnprefixedAttribute= _scala_xml(_UnprefixedAttribute) - private def _scala_xml__Elem = _scala_xml(__Elem) - private def _scala_xml__Text = _scala_xml(__Text) - - /** Wildly wrong documentation deleted in favor of "self-documenting code." */ - protected def mkXML( - pos: Position, - isPattern: Boolean, - pre: Tree, - label: Tree, - attrs: Tree, - scope: Tree, - empty: Boolean, - children: Seq[Tree]): Tree = - { - def starArgs = - if (children.isEmpty) Nil - else List(Typed(makeXMLseq(pos, children), wildStar)) - - def pat = Apply(_scala_xml__Elem, List(pre, label, wild, wild) ::: convertToTextPat(children)) - def nonpat = New(_scala_xml_Elem, List(List(pre, label, attrs, scope, if (empty) Literal(Constant(true)) else Literal(Constant(false))) ::: starArgs)) - - atPos(pos) { if (isPattern) pat else nonpat } - } - - final def entityRef(pos: Position, n: String) = - atPos(pos)( New(_scala_xml_EntityRef, LL(const(n))) ) - - // create scala.xml.Text here <: scala.xml.Node - final def text(pos: Position, txt: String): Tree = atPos(pos) { - if (isPattern) makeTextPat(const(txt)) - else makeText1(const(txt)) - } - - def makeTextPat(txt: Tree) = Apply(_scala_xml__Text, List(txt)) - def makeText1(txt: Tree) = New(_scala_xml_Text, LL(txt)) - def comment(pos: Position, text: String) = atPos(pos)( Comment(const(text)) ) - def charData(pos: Position, txt: String) = atPos(pos)( makeText1(const(txt)) ) - - def procInstr(pos: Position, target: String, txt: String) = - atPos(pos)( ProcInstr(const(target), const(txt)) ) - - protected def Comment(txt: Tree) = New(_scala_xml_Comment, LL(txt)) - protected def ProcInstr(target: Tree, txt: Tree) = New(_scala_xml_ProcInstr, LL(target, txt)) - - /** @todo: attributes */ - def makeXMLpat(pos: Position, n: String, args: Seq[Tree]): Tree = { - val (prepat, labpat) = splitPrefix(n) match { - case (Some(pre), rest) => (const(pre), const(rest)) - case _ => (wild, const(n)) - } - mkXML(pos, true, prepat, labpat, null, null, false, args) - } - - protected def convertToTextPat(t: Tree): Tree = t match { - case _: Literal => makeTextPat(t) - case _ => t - } - protected def convertToTextPat(buf: Seq[Tree]): List[Tree] = - (buf map convertToTextPat).toList - - def parseAttribute(pos: Position, s: String): Tree = { - val ts = scala.xml.Utility.parseAttributeValue(s) map { - case Text(s) => text(pos, s) - case EntityRef(s) => entityRef(pos, s) - } - ts.length match { - case 0 => TypedSplice(tpd.ref(defn.NilModule) withPos pos) - case 1 => ts.head - case _ => makeXMLseq(pos, ts.toList) - } - } - - def isEmptyText(t: Tree) = t match { - case Literal(Constant("")) => true - case _ => false - } - - /** could optimize if args.length == 0, args.length == 1 AND args(0) is <: Node. */ - def makeXMLseq(pos: Position, args: Seq[Tree]) = { - val buffer = ValDef(_buf, TypeTree(), New(_scala_xml_NodeBuffer, ListOfNil)) - val applies = args filterNot isEmptyText map (t => Apply(Select(Ident(_buf), _plus), List(t))) - - atPos(pos)( Block(buffer :: applies.toList, Ident(_buf)) ) - } - - /** Returns (Some(prefix) | None, rest) based on position of ':' */ - def splitPrefix(name: String): (Option[String], String) = splitWhere(name, _ == ':', true) match { - case Some((pre, rest)) => (Some(pre), rest) - case _ => (None, name) - } - - /** Various node constructions. */ - def group(pos: Position, args: Seq[Tree]): Tree = - atPos(pos)( New(_scala_xml_Group, LL(makeXMLseq(pos, args))) ) - - def unparsed(pos: Position, str: String): Tree = - atPos(pos)( New(_scala_xml_Unparsed, LL(const(str))) ) - - def element(pos: Position, qname: String, attrMap: mutable.Map[String, Tree], empty: Boolean, args: Seq[Tree]): Tree = { - def handleNamespaceBinding(pre: String, z: String): Tree = { - def mkAssign(t: Tree): Tree = Assign( - Ident(_tmpscope), - New(_scala_xml_NamespaceBinding, LL(const(pre), t, Ident(_tmpscope))) - ) - - val uri1 = attrMap(z) match { - case Apply(_, List(uri @ Literal(Constant(_)))) => mkAssign(uri) - case Select(_, nme.Nil) => mkAssign(const(null)) // allow for xmlns="" -- bug #1626 - case x => mkAssign(x) - } - attrMap -= z - uri1 - } - - /** Extract all the namespaces from the attribute map. */ - val namespaces: List[Tree] = - for (z <- attrMap.keys.toList ; if z startsWith "xmlns") yield { - val ns = splitPrefix(z) match { - case (Some(_), rest) => rest - case _ => null - } - handleNamespaceBinding(ns, z) - } - - val (pre, newlabel) = splitPrefix(qname) match { - case (Some(p), x) => (p, x) - case (None, x) => (null, x) - } - - def mkAttributeTree(pre: String, key: String, value: Tree) = atPos(pos.toSynthetic) { - // XXX this is where we'd like to put Select(value, nme.toString_) for #1787 - // after we resolve the Some(foo) situation. - val baseArgs = List(const(key), value, Ident(_md)) - val (clazz, attrArgs) = - if (pre == null) (_scala_xml_UnprefixedAttribute, baseArgs) - else (_scala_xml_PrefixedAttribute , const(pre) :: baseArgs) - - Assign(Ident(_md), New(clazz, LL(attrArgs: _*))) - } - - def handlePrefixedAttribute(pre: String, key: String, value: Tree) = mkAttributeTree(pre, key, value) - def handleUnprefixedAttribute(key: String, value: Tree) = mkAttributeTree(null, key, value) - - val attributes: List[Tree] = - for ((k, v) <- attrMap.toList.reverse) yield splitPrefix(k) match { - case (Some(pre), rest) => handlePrefixedAttribute(pre, rest, v) - case _ => handleUnprefixedAttribute(k, v) - } - - lazy val scopeDef = ValDef(_scope, _scala_xml_NamespaceBinding, Ident(_tmpscope)) - lazy val tmpScopeDef = ValDef(_tmpscope, _scala_xml_NamespaceBinding, Ident(_scope)).withFlags(Mutable) - lazy val metadataDef = ValDef(_md, _scala_xml_MetaData, _scala_xml_Null).withFlags(Mutable) - val makeSymbolicAttrs = if (!attributes.isEmpty) Ident(_md) else _scala_xml_Null - - val (attrResult, nsResult) = - (attributes.isEmpty, namespaces.isEmpty) match { - case (true , true) => (Nil, Nil) - case (true , false) => (scopeDef :: Nil, tmpScopeDef :: namespaces) - case (false, true) => (metadataDef :: attributes, Nil) - case (false, false) => (scopeDef :: metadataDef :: attributes, tmpScopeDef :: namespaces) - } - - val body = mkXML( - pos.toSynthetic, - false, - const(pre), - const(newlabel), - makeSymbolicAttrs, - Ident(_scope), - empty, - args - ) - - atPos(pos.toSynthetic)( Block(nsResult, Block(attrResult, body)) ) - } -} diff --git a/src/dotty/tools/dotc/parsing/Tokens.scala b/src/dotty/tools/dotc/parsing/Tokens.scala deleted file mode 100644 index 5324207db..000000000 --- a/src/dotty/tools/dotc/parsing/Tokens.scala +++ /dev/null @@ -1,238 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import collection.immutable.BitSet -import core.Decorators._ - -abstract class TokensCommon { - val maxToken: Int - - type Token = Int - type TokenSet = BitSet - - def tokenRange(lo: Int, hi: Int): TokenSet = BitSet(lo to hi: _*) - - def showTokenDetailed(token: Int) = debugString(token) - - def showToken(token: Int) = { - val str = tokenString(token) - if (keywords contains token) s"'$str'" else str - } - - val tokenString, debugString = new Array[String](maxToken + 1) - - def enter(token: Int, str: String, debugStr: String = ""): Unit = { - assert(tokenString(token) == null) - tokenString(token) = str - debugString(token) = if (debugStr.isEmpty) str else debugStr - } - - /** special tokens */ - final val EMPTY = 0; enter(EMPTY, "<empty>") // a missing token, used in lookahead - final val ERROR = 1; enter(ERROR, "erroneous token") // an erroneous token - final val EOF = 2; enter(EOF, "eof") - - /** literals */ - final val CHARLIT = 3; enter(CHARLIT, "character literal") - final val INTLIT = 4; enter(INTLIT, "integer literal") - final val LONGLIT = 5; enter(LONGLIT, "long literal") - final val FLOATLIT = 6; enter(FLOATLIT, "float literal") - final val DOUBLELIT = 7; enter(DOUBLELIT, "double literal") - final val STRINGLIT = 8; enter(STRINGLIT, "string literal") - final val STRINGPART = 9; enter(STRINGPART, "string literal", "string literal part") - //final val INTERPOLATIONID = 10; enter(INTERPOLATIONID, "string interpolator") - //final val SYMBOLLIT = 11; enter(SYMBOLLIT, "symbol literal") // TODO: deprecate - - /** identifiers */ - final val IDENTIFIER = 12; enter(IDENTIFIER, "identifier") - //final val BACKQUOTED_IDENT = 13; enter(BACKQUOTED_IDENT, "identifier", "backquoted ident") - - /** alphabetic keywords */ - final val IF = 20; enter(IF, "if") - final val FOR = 21; enter(FOR, "for") - final val ELSE = 22; enter(ELSE, "else") - final val THIS = 23; enter(THIS, "this") - final val NULL = 24; enter(NULL, "null") - final val NEW = 25; enter(NEW, "new") - //final val WITH = 26; enter(WITH, "with") - final val SUPER = 27; enter(SUPER, "super") - //final val CASE = 28; enter(CASE, "case") - //final val CASECLASS = 29; enter(CASECLASS, "case class") - //final val CASEOBJECT = 30; enter(CASEOBJECT, "case object") - //final val VAL = 31; enter(VAL, "val") - final val ABSTRACT = 32; enter(ABSTRACT, "abstract") - final val FINAL = 33; enter(FINAL, "final") - final val PRIVATE = 34; enter(PRIVATE, "private") - final val PROTECTED = 35; enter(PROTECTED, "protected") - final val OVERRIDE = 36; enter(OVERRIDE, "override") - //final val IMPLICIT = 37; enter(IMPLICIT, "implicit") - //final val VAR = 38; enter(VAR, "var") - //final val DEF = 39; enter(DEF, "def") - //final val TYPE = 40; enter(TYPE, "type") - final val EXTENDS = 41; enter(EXTENDS, "extends") - final val TRUE = 42; enter(TRUE, "true") - final val FALSE = 43; enter(FALSE, "false") - //final val OBJECT = 44; enter(OBJECT, "object") - final val CLASS = 45; enter(CLASS, "class") - final val IMPORT = 46; enter(IMPORT, "import") - final val PACKAGE = 47; enter(PACKAGE, "package") - //final val YIELD = 48; enter(YIELD, "yield") - final val DO = 49; enter(DO, "do") - //final val TRAIT = 50; enter(TRAIT, "trait") - //final val SEALED = 51; enter(SEALED, "sealed") - final val THROW = 52; enter(THROW, "throw") - final val TRY = 53; enter(TRY, "try") - final val CATCH = 54; enter(CATCH, "catch") - final val FINALLY = 55; enter(FINALLY, "finally") - final val WHILE = 56; enter(WHILE, "while") - final val RETURN = 57; enter(RETURN, "return") - //final val MATCH = 58; enter(MATCH, "match") - //final val LAZY = 59; enter(LAZY, "lazy") - //final val THEN = 60; enter(THEN, "then") - //final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate - //final val INLINE = 62; enter(INLINE, "inline") - - /** special symbols */ - final val COMMA = 70; enter(COMMA, "','") - final val SEMI = 71; enter(SEMI, "';'") - final val DOT = 72; enter(DOT, "'.'") - //final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") - //final val NEWLINES = 79; enter(NEWLINES, "end of statement", "new lines") - - /** special keywords */ - //final val USCORE = 73; enter(USCORE, "_") - final val COLON = 74; enter(COLON, ":") - final val EQUALS = 75; enter(EQUALS, "=") - //final val LARROW = 76; enter(LARROW, "<-") - //final val ARROW = 77; enter(ARROW, "=>") - //final val SUBTYPE = 80; enter(SUBTYPE, "<:") - //final val SUPERTYPE = 81; enter(SUPERTYPE, ">:") - //final val HASH = 82; enter(HASH, "#") - final val AT = 83; enter(AT, "@") - //final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate - - val keywords: TokenSet - - /** parentheses */ - final val LPAREN = 90; enter(LPAREN, "'('") - final val RPAREN = 91; enter(RPAREN, "')'") - final val LBRACKET = 92; enter(LBRACKET, "'['") - final val RBRACKET = 93; enter(RBRACKET, "']'") - final val LBRACE = 94; enter(LBRACE, "'{'") - final val RBRACE = 95; enter(RBRACE, "'}'") - - final val firstParen = LPAREN - final val lastParen = RBRACE - - def buildKeywordArray(keywords: TokenSet) = { - def start(tok: Token) = tokenString(tok).toTermName.start - def sourceKeywords = keywords.toList.filter { (kw: Token) => - val ts = tokenString(kw) - (ts != null) && !ts.contains(' ') - } - - val lastKeywordStart = sourceKeywords.map(start).max - - val arr = Array.fill(lastKeywordStart + 1)(IDENTIFIER) - for (kw <- sourceKeywords) arr(start(kw)) = kw - (lastKeywordStart, arr) - } -} - -object Tokens extends TokensCommon { - final val minToken = EMPTY - final val maxToken = XMLSTART - - final val INTERPOLATIONID = 10; enter(INTERPOLATIONID, "string interpolator") - final val SYMBOLLIT = 11; enter(SYMBOLLIT, "symbol literal") // TODO: deprecate - - final val BACKQUOTED_IDENT = 13; enter(BACKQUOTED_IDENT, "identifier", "backquoted ident") - - final val identifierTokens = BitSet(IDENTIFIER, BACKQUOTED_IDENT) - - def isIdentifier(token : Int) = - token >= IDENTIFIER && token <= BACKQUOTED_IDENT - - /** alphabetic keywords */ - final val WITH = 26; enter(WITH, "with") - final val CASE = 28; enter(CASE, "case") - final val CASECLASS = 29; enter(CASECLASS, "case class") - final val CASEOBJECT = 30; enter(CASEOBJECT, "case object") - final val VAL = 31; enter(VAL, "val") - final val IMPLICIT = 37; enter(IMPLICIT, "implicit") - final val VAR = 38; enter(VAR, "var") - final val DEF = 39; enter(DEF, "def") - final val TYPE = 40; enter(TYPE, "type") - final val OBJECT = 44; enter(OBJECT, "object") - final val YIELD = 48; enter(YIELD, "yield") - final val TRAIT = 50; enter(TRAIT, "trait") - final val SEALED = 51; enter(SEALED, "sealed") - final val MATCH = 58; enter(MATCH, "match") - final val LAZY = 59; enter(LAZY, "lazy") - final val THEN = 60; enter(THEN, "then") - final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate - final val INLINE = 62; enter(INLINE, "inline") - - /** special symbols */ - final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") - final val NEWLINES = 79; enter(NEWLINES, "end of statement", "new lines") - - /** special keywords */ - final val USCORE = 73; enter(USCORE, "_") - final val LARROW = 76; enter(LARROW, "<-") - final val ARROW = 77; enter(ARROW, "=>") - final val SUBTYPE = 80; enter(SUBTYPE, "<:") - final val SUPERTYPE = 81; enter(SUPERTYPE, ">:") - final val HASH = 82; enter(HASH, "#") - final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate - - /** XML mode */ - final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate - - final val alphaKeywords = tokenRange(IF, INLINE) - final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND) - final val symbolicTokens = tokenRange(COMMA, VIEWBOUND) - final val keywords = alphaKeywords | symbolicKeywords - - final val allTokens = tokenRange(minToken, maxToken) - - final val simpleLiteralTokens = tokenRange(CHARLIT, STRINGLIT) | BitSet(TRUE, FALSE) - final val literalTokens = simpleLiteralTokens | BitSet(INTERPOLATIONID, SYMBOLLIT, NULL) - - final val atomicExprTokens = literalTokens | identifierTokens | BitSet( - USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, XMLSTART) - - final val canStartExpressionTokens = atomicExprTokens | BitSet( - LBRACE, LPAREN, IF, DO, WHILE, FOR, NEW, TRY, THROW) - - final val canStartTypeTokens = literalTokens | identifierTokens | BitSet( - THIS, SUPER, USCORE, LPAREN, AT) - - final val templateIntroTokens = BitSet(CLASS, TRAIT, OBJECT, CASECLASS, CASEOBJECT) - - final val dclIntroTokens = BitSet(DEF, VAL, VAR, TYPE) - - final val defIntroTokens = templateIntroTokens | dclIntroTokens - - final val localModifierTokens = BitSet( - ABSTRACT, FINAL, SEALED, IMPLICIT, INLINE, LAZY) - - final val accessModifierTokens = BitSet( - PRIVATE, PROTECTED) - - final val modifierTokens = localModifierTokens | accessModifierTokens | BitSet( - OVERRIDE) - - /** Is token only legal as start of statement (eof also included)? */ - final val mustStartStatTokens = defIntroTokens | modifierTokens | BitSet( - IMPORT, PACKAGE) - - final val canStartStatTokens = canStartExpressionTokens | mustStartStatTokens | BitSet( - AT, CASE) - - final val canEndStatTokens = atomicExprTokens | BitSet( - TYPE, RPAREN, RBRACE, RBRACKET) - - final val numericLitTokens = BitSet(INTLIT, LONGLIT, FLOATLIT, DOUBLELIT) -} diff --git a/src/dotty/tools/dotc/parsing/TreeBuilder.scala.unused b/src/dotty/tools/dotc/parsing/TreeBuilder.scala.unused deleted file mode 100644 index 672c85179..000000000 --- a/src/dotty/tools/dotc/parsing/TreeBuilder.scala.unused +++ /dev/null @@ -1,535 +0,0 @@ -package dotty.tools -package dotc -package parsing - -import core._ -import Flags._, Trees._, TypedTrees._, UntypedTrees._, Names._, StdNames._, NameOps._, Contexts._ -import scala.collection.mutable.ListBuffer -import util.Positions._, Symbols._, Decorators._, Flags._, Constants._ -import TreeInfo._ - -/** Methods for building trees, used in the parser. All the trees - * returned by this class must be untyped. - * Note: currently unused - */ -class TreeBuilder(implicit ctx: Context) { - - import untpd._ - - def scalaDot(name: Name): Select = - Select(new TypedSplice(tpd.Ident(defn.ScalaPackageVal.termRef)), name) - - def scalaAnyRefConstr = scalaDot(tpnme.AnyRef) - def scalaAnyValConstr = scalaDot(tpnme.AnyVal) - def scalaAnyConstr = scalaDot(tpnme.Any) - def scalaUnitConstr = scalaDot(tpnme.Unit) - def productConstr = scalaDot(tpnme.Product) - def productConstrN(n: Int) = scalaDot(("Product" + n).toTypeName) - def serializableConstr = scalaDot(tpnme.Serializable) - - def convertToTypeName(t: Tree): Tree = ??? - - private implicit val cpos = NoPosition - - /** Convert all occurrences of (lower-case) variables in a pattern as follows: - * x becomes x @ _ - * x: T becomes x @ (_: T) - * Also covert all toplevel lower-case type arguments as follows: - * t becomes t @ _ - */ - private object patvarTransformer extends TreeTransformer { - override def transform(tree: Tree): Tree = tree match { - case Ident(name) if isVarPattern(tree) && name != nme.WILDCARD => - Bind( - name, Ident(nme.WILDCARD).withPos(tree.pos.focus) - ).withPos(tree.pos) - case Typed(id @ Ident(name), tpt) if isVarPattern(id) && name != nme.WILDCARD => - Bind( - name, - Typed( - Ident(nme.WILDCARD).withPos(tree.pos.focus), - transform(tpt) - ).withPos(tree.pos.withStart(tree.pos.point)) - ).withPos(tree.pos.withPoint(id.pos.point)) - case Apply(fn @ Apply(_, _), args) => - tree.derivedApply(transform(fn), transform(args)) - case Apply(fn, args) => - tree.derivedApply(fn, transform(args)) - case Typed(expr, tpt) => - tree.derivedTyped(transform(expr), transform(tpt)) - case Bind(name, body) => - tree.derivedBind(name, transform(body)) - case AppliedTypeTree(tycon, args) => - tree.derivedAppliedTypeTree(tycon, args map transform) - case Alternative(_) | Typed(_, _) | AndTypeTree(_, _) | Annotated(_, _) => - super.transform(tree) - case Parens(_) => - stripParens(tree) - case _ => - tree - } - } - - case class VariableInfo(name: Name, tree: Tree, pos: Position) - - /** Traverse pattern and collect all variable names with their types in buffer - * The variables keep their positions; whereas the pattern is converted to be - * synthetic for all nodes that contain a variable position. - */ - object getVars extends TreeAccumulator[ListBuffer[VariableInfo]] { - - def namePos(tree: Tree, name: Name): Position = - if (name contains '$') tree.pos.focus - else { - val start = tree.pos.start - val end = start + name.decode.length - Position(start, end) - } - - override def apply(buf: ListBuffer[VariableInfo], tree: Tree): ListBuffer[VariableInfo] = { - def seenName(name: Name) = buf exists (_.name == name) - def add(name: Name, t: Tree): ListBuffer[VariableInfo] = - if (seenName(name)) buf else buf += VariableInfo(name, t, namePos(tree, name)) - - tree match { - case Bind(nme.WILDCARD, _) => - foldOver(buf, tree) - case Bind(name, Typed(tree1, tpt)) if !mayBeTypePat(tpt) => - apply(add(name, tpt), tree1) - case Bind(name, tree1) => - apply(add(name, TypeTree()), tree1) - case _ => - foldOver(buf, tree) - } - } - } - - /** Returns list of all pattern variables, possibly with their types, - * without duplicates - */ - private def getVariables(tree: Tree): List[VariableInfo] = - getVars(new ListBuffer[VariableInfo], tree).toList - - def byNameApplication(tpe: Tree): Tree = - AppliedTypeTree(scalaDot(tpnme.BYNAME_PARAM_CLASS), List(tpe)) - def repeatedApplication(tpe: Tree): Tree = - AppliedTypeTree(scalaDot(tpnme.REPEATED_PARAM_CLASS), List(tpe)) - - def makeTuple(trees: List[Tree])(implicit cpos: Position): Tree = { - def mkPair(t1: Tree, t2: Tree) = { - if (t1.isType) AppliedTypeTree(scalaDot(tpnme.Pair), List(t1, t2)) - else Pair(t1, t2) - } - trees reduce mkPair - } - - def stripParens(t: Tree) = t match { - case Parens(t) => t - case _ => t - } - - def makeSelfDef(name: TermName, tpt: Tree): ValDef = - ValDef(Modifiers(Private), name, tpt, EmptyTree()) - - /** If tree is a variable pattern, return its variable info. - * Otherwise return none. - */ - private def matchVarPattern(tree: Tree): Option[VariableInfo] = { - def wildType(t: Tree): Option[Tree] = t match { - case Ident(x) if x.toTermName == nme.WILDCARD => Some(TypeTree()) - case Typed(Ident(x), tpt) if x.toTermName == nme.WILDCARD => Some(tpt) - case _ => None - } - tree match { - case Ident(name) => Some(VariableInfo(name, TypeTree(), tree.pos)) - case Bind(name, body) => wildType(body) map (x => VariableInfo(name, x, tree.pos)) - case Typed(id @ Ident(name), tpt) => Some(VariableInfo(name, tpt, id.pos)) - case _ => None - } - } - - /** Create tree representing (unencoded) binary operation expression or pattern. */ - def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { - def mkNamed(args: List[Tree]) = - if (isExpr) args map { - case arg @ Assign(Ident(name), rhs) => NamedArg(name, rhs).withPos(arg.pos) - case arg => arg - } else args - val arguments = right match { - case Parens(arg) => mkNamed(arg :: Nil) - case _ => right :: Nil - } - if (isExpr) { - if (isLeftAssoc(op)) { - Apply(Select(stripParens(left), op.encode).withPos(opPos), arguments) - } else { - val x = ctx.freshName().toTermName - Block( - List(ValDef(Modifiers(Synthetic), x, TypeTree(), stripParens(left))), - Apply(Select(stripParens(right), op.encode).withPos(opPos), List(Ident(x).withPos(left.pos)))) - } - } else { - Apply(Ident(op.encode).withPos(opPos), stripParens(left) :: arguments) - } - } - - /** tpt.<init> */ - def SelectConstructor(tpt: Tree): Tree = - Select(tpt, nme.CONSTRUCTOR) - - private def splitArgss(constr: Tree, outerArgss: List[List[Tree]]): (Tree, List[List[Tree]]) = constr match { - case Apply(tree, args) => splitArgss(tree, args :: outerArgss) - case _ => (constr, if (outerArgss.isEmpty) ListOfNil else outerArgss) - } - - /** new tpt(argss_1)...(argss_n) - * @param npos the position spanning <new tpt>, without any arguments - */ - def makeNew(parentConstr: Tree) = { - val (tpt, argss) = splitArgss(parentConstr, Nil) - New(tpt, argss) - } - - /** Create positioned tree representing an object creation <new parents { self => stats } - */ - def makeNew(templ: Template): Tree = { - val x = tpnme.ANON_CLASS - val nu = makeNew(Ident(x)) - val clsDef = { - implicit val cpos = NoPosition - ClassDef(Modifiers(Final), x, Nil, templ) - } - Block(clsDef, nu) - } - - /** Create positioned tree representing an object creation <new parents { self => stats } - * @param cpos the position of the new, focus should be the first parent's start. - */ - def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree]): Tree = { - val newPos = Position(cpos.start, cpos.point) - val clsPos = Position(cpos.point, cpos.end) - if (parents.isEmpty) - makeNew(List(scalaAnyRefConstr.withPos(newPos.endPos)), self, stats) - else if (parents.tail.isEmpty && stats.isEmpty) - makeNew(parents.head) - else { - val x = tpnme.ANON_CLASS - val nu = makeNew(Ident(x).withPos(newPos)).withPos(newPos) - val clsDef = { - implicit val cpos = clsPos - ClassDef(Modifiers(Final), x, Nil, Template(???, parents, self, stats)) - } - Block(clsDef, nu) - } - } - - /** Create a tree representing an assignment <lhs = rhs> */ - def makeAssign(lhs: Tree, rhs: Tree): Tree = lhs match { - case Apply(fn, args) => - Apply(Select(fn, nme.update), args :+ rhs) - case _ => - Assign(lhs, rhs) - } - - /** A type tree corresponding to (possibly unary) intersection type - def makeIntersectionTypeTree(tps: List[Tree]): Tree = - if (tps.tail.isEmpty) tps.head - else CompoundTypeTree(Template(tps, emptyValDef, Nil))*/ - - private def labelDefAndCall(lname: TermName, rhs: Tree, call: Tree) = { - val ldef = DefDef(Modifiers(Label).withPos(cpos.startPos), lname, Nil, ListOfNil, TypeTree(), rhs) - Block(ldef, call) - } - - private def labelCall(lname: TermName): Apply = - Apply(Ident(lname), Nil) - - /** Create tree representing a while loop */ - def makeWhile(lname: TermName, cond: Tree, body: Tree): Tree = { - val continu = labelCall(lname).withPos((cond.pos union body.pos).endPos) - val rhs = { - implicit val cpos = NoPosition - If(cond, Block(body, continu), Literal(Constant()).withPos(continu.pos)) - } - labelDefAndCall(lname, rhs, continu) - } - - /** Create tree representing a do-while loop */ - def makeDoWhile(lname: TermName, body: Tree, cond: Tree): Tree = { - val continu = labelCall(lname).withPos((cond.pos union body.pos).endPos) - val rhs = Block(body, If(cond, continu, Literal(Constant()).withPos(continu.pos))) - labelDefAndCall(lname, rhs, continu) - } - - /** Create block of statements `stats` */ - def makeBlock(stats: List[Tree]): Tree = - if (stats.isEmpty) Literal(Constant()) - else if (!stats.last.isTerm) Block(stats, Literal(Constant()).withPos(cpos.endPos)) - else if (stats.length == 1) stats.head - else Block(stats.init, stats.last) - - def makePatFilter(tree: Tree, condition: Tree, canDrop: Boolean): Tree = { - val cases = List( - CaseDef(condition, EmptyTree(), Literal(Constant(true))), - CaseDef(Ident(nme.WILDCARD), EmptyTree(), Literal(Constant(false))) - ) - val matchTree = makeVisitor(cases, checkExhaustive = false, canDrop) - locally { - implicit val cpos = tree.pos - Apply(Select(tree, nme.withFilter), matchTree :: Nil) - } - } - - /** Create tree for for-comprehension generator <pat <- rhs> or <pat = rhs> */ - def makeGenerator(pat: Tree, valeq: Boolean, rhs: Tree): Enumerator = { - val pat1 = patvarTransformer.transform(pat) - if (valeq) ValEq(pat1, rhs) - else ValFrom(pat1, makePatFilter(rhs, pat1, canDrop = true)) - } - -/* - def makeSyntheticTypeParam(pname: TypeName, bounds: Tree) = - TypeDef(Modifiers(DEFERRED | SYNTHETIC), pname, Nil, bounds) -*/ - abstract class Enumerator { def pos: Position } - case class ValFrom(pat: Tree, rhs: Tree) extends Enumerator { - val pos = cpos union pat.pos union rhs.pos - } - case class ValEq(pat: Tree, rhs: Tree) extends Enumerator { - val pos = cpos union pat.pos union rhs.pos - } - case class Filter(test: Tree) extends Enumerator { - val pos = cpos union test.pos - } - - /** Create tree for for-comprehension <for (enums) do body> or - * <for (enums) yield body> where mapName and flatMapName are chosen - * corresponding to whether this is a for-do or a for-yield. - * The creation performs the following rewrite rules: - * - * 1. - * - * for (P <- G) E ==> G.foreach (P => E) - * - * Here and in the following (P => E) is interpreted as the function (P => E) - * if P is a variable pattern and as the partial function { case P => E } otherwise. - * - * 2. - * - * for (P <- G) yield E ==> G.map (P => E) - * - * 3. - * - * for (P_1 <- G_1; P_2 <- G_2; ...) ... - * ==> - * G_1.flatMap (P_1 => for (P_2 <- G_2; ...) ...) - * - * 4. - * - * for (P <- G; E; ...) ... - * => - * for (P <- G.filter (P => E); ...) ... - * - * 5. For any N: - * - * for (P_1 <- G; P_2 = E_2; val P_N = E_N; ...) - * ==> - * for (TupleN(P_1, P_2, ... P_N) <- - * for (x_1 @ P_1 <- G) yield { - * val x_2 @ P_2 = E_2 - * ... - * val x_N & P_N = E_N - * TupleN(x_1, ..., x_N) - * } ...) - * - * If any of the P_i are variable patterns, the corresponding `x_i @ P_i' is not generated - * and the variable constituting P_i is used instead of x_i - * - * @param mapName The name to be used for maps (either map or foreach) - * @param flatMapName The name to be used for flatMaps (either flatMap or foreach) - * @param enums The enumerators in the for expression - * @param body The body of the for expression - */ - private def makeFor(mapName: TermName, flatMapName: TermName, enums: List[Enumerator], body: Tree): Tree = { - - /** make a closure pat => body. - * The closure is assigned a transparent position with the point at pos.point and - * the limits given by pat and body. - */ - def makeClosure(pat: Tree, body: Tree): Tree = - matchVarPattern(pat) match { - case Some(VariableInfo(name, tpt, pos)) => - Function(ValDef(Modifiers(Param).withPos(cpos.startPos), name.toTermName, tpt, EmptyTree()).withPos(pos) :: Nil, body) - case None => - makeVisitor(List(CaseDef(pat, EmptyTree(), body)), checkExhaustive = false) - } - - /** Make an application qual.meth(pat => body) positioned at `pos`. - */ - def makeCombination(meth: TermName, qual: Tree, pat: Tree, body: Tree): Tree = - Apply(Select(qual, meth).withPos(NoPosition), makeClosure(pat, body)) - - /** Optionally, if pattern is a `Bind`, the bound name, otherwise None. - */ - def patternVar(pat: Tree): Option[Name] = pat match { - case Bind(name, _) => Some(name) - case _ => None - } - - /** If `pat` is not yet a `Bind` wrap it in one with a fresh name - */ - def makeBind(pat: Tree): Tree = pat match { - case Bind(_, _) => pat - case _ => Bind(ctx.freshName().toTermName, pat) - } - - /** A reference to the name bound in Bind `pat`. - */ - def makeValue(pat: Tree): Tree = pat match { - case Bind(name, _) => Ident(name).withPos(pat.pos.focus) - } - - enums match { - case (enum @ ValFrom(pat, rhs)) :: Nil => - makeCombination(mapName, rhs, pat, body).withPos(enum.pos) - case ValFrom(pat, rhs) :: (rest @ (ValFrom( _, _) :: _)) => - makeCombination(flatMapName, rhs, pat, - makeFor(mapName, flatMapName, rest, body)) - case (enum @ ValFrom(pat, rhs)) :: Filter(test) :: rest => - makeFor(mapName, flatMapName, - ValFrom(pat, makeCombination(nme.withFilter, rhs, pat, test)) :: rest, - body) - case (enum @ ValFrom(pat, rhs)) :: rest => - val (valeqs, rest1) = rest.span(_.isInstanceOf[ValEq]) - assert(!valeqs.isEmpty) - val pats = valeqs map { case ValEq(pat, _) => pat } - val rhss = valeqs map { case ValEq(_, rhs) => rhs } - val defpat1 = makeBind(pat) - val defpats = pats map makeBind - val pdefs = (defpats, rhss).zipped flatMap (makePatDef) - val ids = (defpat1 :: defpats) map makeValue - val rhs1 = makeForYield(ValFrom(defpat1, rhs) :: Nil, Block(pdefs, makeTuple(ids))) - val allpats = pat :: pats - val vfrom1 = ValFrom(makeTuple(allpats), rhs1) - makeFor(mapName, flatMapName, vfrom1 :: rest1, body) - case _ => - EmptyTree() //may happen for erroneous input - } - } - - /** Create tree for for-do comprehension <for (enums) body> */ - def makeFor(enums: List[Enumerator], body: Tree): Tree = - makeFor(nme.foreach, nme.foreach, enums, body) - - /** Create tree for for-yield comprehension <for (enums) yield body> */ - def makeForYield(enums: List[Enumerator], body: Tree): Tree = - makeFor(nme.map, nme.flatMap, enums, body) - - /** Create tree for a pattern alternative */ - def makeAlternative(ts: List[Tree]): Tree = Alternative(ts flatMap alternatives) - - def alternatives(t: Tree): List[Tree] = t match { - case Alternative(ts) => ts - case _ => List(t) - } - - def mkAnnotated(cls: Symbol, tree: Tree) = - Annotated(TypedSplice(tpd.New(cls.typeRef)), tree) - - /** Create visitor <x => x match cases> */ - def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean, canDrop: Boolean = false): Tree = { - val x = ctx.freshName().toTermName - val id = Ident(x) - val sel = - if (canDrop) mkAnnotated(???, id) - else if (!checkExhaustive) mkAnnotated(defn.UncheckedAnnot, id) - else id - Function(List(ugen.syntheticParameter(x)), Match(sel, cases)) - } - - /** Create tree for case definition <case pat if guard => rhs> */ - def makeCaseDef(pat: Tree, guard: Tree, rhs: Tree): CaseDef = - CaseDef(patvarTransformer.transform(pat), guard, rhs) - - /** Create tree for pattern definition <val pat0 = rhs> */ - def makePatDef(pat: Tree, rhs: Tree): List[Tree] = - makePatDef(Modifiers(), pat, rhs) - - /** Create tree for pattern definition <mods val pat0 = rhs> */ - def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree, varsArePatterns: Boolean = false): List[Tree] = matchVarPattern(pat) match { - case Some(VariableInfo(name, tpt, pos)) if varsArePatterns => - ValDef(mods, name.toTermName, tpt, rhs).withPos(pos) :: Nil // point comes from pat.pos - - case _ => - // in case there is exactly one variable x_1 in pattern - // val/var p = e ==> val/var x_1 = e.match (case p => (x_1)) - // - // in case there are zero or more than one variables in pattern - // val/var p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) - // val/var x_1 = t$._1 - // ... - // val/var x_N = t$._N - - val rhsUnchecked = mkAnnotated(defn.UncheckedAnnot, rhs) - - // TODO: clean this up -- there is too much information packed into makePatDef's `pat` argument - // when it's a simple identifier (case Some((name, tpt)) -- above), - // pat should have the type ascription that was specified by the user - // however, in `case None` (here), we must be careful not to generate illegal pattern trees (such as `(a, b): Tuple2[Int, String]`) - // i.e., this must hold: pat1 match { case Typed(expr, tp) => assert(expr.isInstanceOf[Ident]) case _ => } - // if we encounter such an erroneous pattern, we strip off the type ascription from pat and propagate the type information to rhs - val (pat1, rhs1) = patvarTransformer.transform(pat) match { - // move the Typed ascription to the rhs - case Typed(expr, tpt) if !expr.isInstanceOf[Ident] => - val rhsTypedUnchecked = - if (tpt.isEmpty) rhsUnchecked else Typed(rhsUnchecked, tpt) - (expr, rhsTypedUnchecked) - case ok => - (ok, rhsUnchecked) - } - val vars = getVariables(pat1) - val ids = vars map (v => Ident(v.name).withPos(v.pos)) - val caseDef = CaseDef(pat1, EmptyTree(), makeTuple(ids)) - val matchExpr = Match(rhs1, caseDef :: Nil) - vars match { - case List(VariableInfo(vname, tpt, pos)) => - ValDef(mods, vname.toTermName, tpt, matchExpr) :: Nil - case _ => - val tmpName = ctx.freshName().toTermName - val patMods = Modifiers(PrivateLocal | Synthetic | (mods.flags & Lazy)) - val firstDef = ValDef(patMods, tmpName, TypeTree(), matchExpr) - val restDefs = for { - (VariableInfo(vname, tpt, pos), n) <- vars.zipWithIndex - } yield { - val rhs = { - implicit val cpos = pos.focus - Select(Ident(tmpName), ("_" + n).toTermName) - } - ValDef(mods, vname.toTermName, tpt, rhs).withPos(pos) - } - firstDef :: restDefs - } - } - - /** Create a tree representing the function type (argtpes) => restpe */ - def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = - AppliedTypeTree(scalaDot(("Function" + argtpes.length).toTypeName), argtpes ::: List(restpe)) - - /** Append implicit parameter section if `contextBounds` nonempty */ - def addEvidenceParams(owner: Name, vparamss: List[List[ValDef]], contextBounds: List[Tree]): List[List[ValDef]] = { - if (contextBounds.isEmpty) vparamss - else { - val mods = Modifiers(if (owner.isTypeName) PrivateLocal | ParamAccessor else Param) - val evidenceParams = for (tpt <- contextBounds) yield { - val pname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName - ValDef(mods | Implicit | Synthetic, pname, tpt, EmptyTree()) - } - vparamss.reverse match { - case (vparams @ (vparam :: _)) :: _ if vparam.mods is Implicit => - vparamss.init :+ (evidenceParams ++ vparams) - case _ => - vparamss :+ evidenceParams - } - } - } -} diff --git a/src/dotty/tools/dotc/parsing/Utility.scala b/src/dotty/tools/dotc/parsing/Utility.scala deleted file mode 100644 index f522492f8..000000000 --- a/src/dotty/tools/dotc/parsing/Utility.scala +++ /dev/null @@ -1,170 +0,0 @@ -package dotty.tools.dotc.parsing - -import scala.collection.mutable - - -/** - * The `Utility` object provides utility functions for processing instances - * of bound and not bound XML classes, as well as escaping text nodes. - * - * @author Burak Emir - */ -object Utility { - import scala.reflect.internal.Chars.SU - - private val unescMap = Map( - "lt" -> '<', - "gt" -> '>', - "amp" -> '&', - "quot" -> '"', - "apos" -> '\'' - ) - - /** - * Appends unescaped string to `s`, `amp` becomes `&`, - * `lt` becomes `<` etc.. - * - * @return `'''null'''` if `ref` was not a predefined entity. - */ - private final def unescape(ref: String, s: StringBuilder): StringBuilder = - ((unescMap get ref) map (s append _)).orNull - - def parseAttributeValue[T](value: String, text: String => T, entityRef: String => T): List[T] = { - val sb = new StringBuilder - var rfb: StringBuilder = null - val nb = new mutable.ListBuffer[T]() - - val it = value.iterator - while (it.hasNext) { - var c = it.next() - // entity! flush buffer into text node - if (c == '&') { - c = it.next() - if (c == '#') { - c = it.next() - val theChar = parseCharRef ({ ()=> c },{ () => c = it.next() },{s => throw new RuntimeException(s)}, {s => throw new RuntimeException(s)}) - sb.append(theChar) - } - else { - if (rfb eq null) rfb = new StringBuilder() - rfb append c - c = it.next() - while (c != ';') { - rfb.append(c) - c = it.next() - } - val ref = rfb.toString() - rfb.clear() - unescape(ref,sb) match { - case null => - if (!sb.isEmpty) { // flush buffer - nb += text(sb.toString()) - sb.clear() - } - nb += entityRef(ref) // add entityref - case _ => - } - } - } - else sb append c - } - - if (!sb.isEmpty) // flush buffer - nb += text(sb.toString()) - - nb.toList - } - - /** - * {{{ - * CharRef ::= "&#" '0'..'9' {'0'..'9'} ";" - * | "&#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";" - * }}} - * See [66] - */ - def parseCharRef(ch: () => Char, nextch: () => Unit, reportSyntaxError: String => Unit, reportTruncatedError: String => Unit): String = { - val hex = ch() == 'x' - if (hex) nextch() - val base = if (hex) 16 else 10 - var i = 0 - while (ch() != ';') { - ch() match { - case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => - i = i * base + ch().asDigit - case 'a' | 'b' | 'c' | 'd' | 'e' | 'f' - | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' => - if (! hex) - reportSyntaxError("hex char not allowed in decimal char ref\n" + - "Did you mean to write &#x ?") - else - i = i * base + ch().asDigit - case SU => - reportTruncatedError("") - case _ => - reportSyntaxError("character '" + ch() + "' not allowed in char ref\n") - } - nextch() - } - new String(Array(i), 0, 1) - } - - /** {{{ - * (#x20 | #x9 | #xD | #xA) - * }}} */ - final def isSpace(ch: Char): Boolean = ch match { - case '\u0009' | '\u000A' | '\u000D' | '\u0020' => true - case _ => false - } - /** {{{ - * (#x20 | #x9 | #xD | #xA)+ - * }}} */ - final def isSpace(cs: Seq[Char]): Boolean = cs.nonEmpty && (cs forall isSpace) - - /** {{{ - * NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' - * | CombiningChar | Extender - * }}} - * See [4] and Appendix B of XML 1.0 specification. - */ - def isNameChar(ch: Char) = { - import java.lang.Character._ - // The constants represent groups Mc, Me, Mn, Lm, and Nd. - - isNameStart(ch) || (getType(ch).toByte match { - case COMBINING_SPACING_MARK | - ENCLOSING_MARK | NON_SPACING_MARK | - MODIFIER_LETTER | DECIMAL_DIGIT_NUMBER => true - case _ => ".-:" contains ch - }) - } - - /** {{{ - * NameStart ::= ( Letter | '_' ) - * }}} - * where Letter means in one of the Unicode general - * categories `{ Ll, Lu, Lo, Lt, Nl }`. - * - * We do not allow a name to start with `:`. - * See [3] and Appendix B of XML 1.0 specification - */ - def isNameStart(ch: Char) = { - import java.lang.Character._ - - getType(ch).toByte match { - case LOWERCASE_LETTER | - UPPERCASE_LETTER | OTHER_LETTER | - TITLECASE_LETTER | LETTER_NUMBER => true - case _ => ch == '_' - } - } - - /** {{{ - * Name ::= ( Letter | '_' ) (NameChar)* - * }}} - * See [5] of XML 1.0 specification. - */ - def isName(s: String) = - s.nonEmpty && isNameStart(s.head) && (s.tail forall isNameChar) - -} - diff --git a/src/dotty/tools/dotc/parsing/package.scala b/src/dotty/tools/dotc/parsing/package.scala deleted file mode 100644 index 8b113ed96..000000000 --- a/src/dotty/tools/dotc/parsing/package.scala +++ /dev/null @@ -1,33 +0,0 @@ -package dotty.tools.dotc - -import util.Chars._ -import core.Names.Name -import core.StdNames.nme -import core.NameOps._ - -package object parsing { - - def precedence(operator: Name): Int = - if (operator eq nme.ERROR) -1 - else { - val firstCh = operator(0) - if (isScalaLetter(firstCh)) 1 - else if (operator.isOpAssignmentName) 0 - else firstCh match { - case '|' => 2 - case '^' => 3 - case '&' => 4 - case '=' | '!' => 5 - case '<' | '>' => 6 - case ':' => 7 - case '+' | '-' => 8 - case '*' | '/' | '%' => 9 - case _ => 10 - } - } - - def minPrec = 0 - def minInfixPrec = 1 - def maxPrec = 11 - -} diff --git a/src/dotty/tools/dotc/printing/Formatting.scala b/src/dotty/tools/dotc/printing/Formatting.scala deleted file mode 100644 index b321d3736..000000000 --- a/src/dotty/tools/dotc/printing/Formatting.scala +++ /dev/null @@ -1,258 +0,0 @@ -package dotty.tools.dotc -package printing - -import core._ -import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Contexts._ -import collection.mutable -import collection.Map -import Decorators._ -import scala.annotation.switch -import scala.util.control.NonFatal -import reporting.diagnostic.MessageContainer -import util.DiffUtil -import Highlighting._ -import SyntaxHighlighting._ - -object Formatting { - - /** General purpose string formatter, with the following features: - * - * 1) On all Showables, `show` is called instead of `toString` - * 2) Exceptions raised by a `show` are handled by falling back to `toString`. - * 3) Sequences can be formatted using the desired separator between two `%` signs, - * eg `i"myList = (${myList}%, %)"` - * 4) Safe handling of multi-line margins. Left margins are skipped om the parts - * of the string context *before* inserting the arguments. That way, we guard - * against accidentally treating an interpolated value as a margin. - */ - class StringFormatter(protected val sc: StringContext) { - - protected def showArg(arg: Any)(implicit ctx: Context): String = arg match { - case arg: Showable => - try arg.show - catch { - case NonFatal(ex) => s"[cannot display due to $ex, raw string = $toString]" - } - case _ => arg.toString - } - - private def treatArg(arg: Any, suffix: String)(implicit ctx: Context): (Any, String) = arg match { - case arg: Seq[_] if suffix.nonEmpty && suffix.head == '%' => - val (rawsep, rest) = suffix.tail.span(_ != '%') - val sep = StringContext.treatEscapes(rawsep) - if (rest.nonEmpty) (arg.map(showArg).mkString(sep), rest.tail) - else (arg, suffix) - case _ => - (showArg(arg), suffix) - } - - def assemble(args: Seq[Any])(implicit ctx: Context): String = { - def isLineBreak(c: Char) = c == '\n' || c == '\f' // compatible with StringLike#isLineBreak - def stripTrailingPart(s: String) = { - val (pre, post) = s.span(c => !isLineBreak(c)) - pre ++ post.stripMargin - } - val (prefix, suffixes) = sc.parts.toList match { - case head :: tail => (head.stripMargin, tail map stripTrailingPart) - case Nil => ("", Nil) - } - val (args1, suffixes1) = (args, suffixes).zipped.map(treatArg(_, _)).unzip - new StringContext(prefix :: suffixes1.toList: _*).s(args1: _*) - } - } - - /** The `em` string interpolator works like the `i` string interpolator, but marks nonsensical errors - * using `<nonsensical>...</nonsensical>` tags. - * Note: Instead of these tags, it would be nicer to return a data structure containing the message string - * and a boolean indicating whether the message is sensical, but then we cannot use string operations - * like concatenation, stripMargin etc on the values returned by em"...", and in the current error - * message composition methods, this is crucial. - */ - class ErrorMessageFormatter(sc: StringContext) extends StringFormatter(sc) { - override protected def showArg(arg: Any)(implicit ctx: Context): String = - wrapNonSensical(arg, super.showArg(arg)) - } - - class SyntaxFormatter(sc: StringContext) extends StringFormatter(sc) { - override protected def showArg(arg: Any)(implicit ctx: Context): String = - arg match { - case arg: Showable if ctx.settings.color.value != "never" => - val highlighted = - SyntaxHighlighting(wrapNonSensical(arg, super.showArg(arg))) - new String(highlighted.toArray) - case hl: Highlight => - hl.show - case hb: HighlightBuffer => - hb.toString - case str: String if ctx.settings.color.value != "never" => - new String(SyntaxHighlighting(str).toArray) - case _ => super.showArg(arg) - } - } - - private def wrapNonSensical(arg: Any /* Type | Symbol */, str: String)(implicit ctx: Context): String = { - import MessageContainer._ - def isSensical(arg: Any): Boolean = arg match { - case tpe: Type => - tpe.exists && !tpe.isErroneous - case sym: Symbol if sym.isCompleted => - sym.info != ErrorType && sym.info != TypeAlias(ErrorType) && sym.info.exists - case _ => true - } - - if (isSensical(arg)) str - else nonSensicalStartTag + str + nonSensicalEndTag - } - - private type Recorded = AnyRef /*Symbol | PolyParam*/ - - private class Seen extends mutable.HashMap[String, List[Recorded]] { - - override def default(key: String) = Nil - - def record(str: String, entry: Recorded)(implicit ctx: Context): String = { - def followAlias(e1: Recorded): Recorded = e1 match { - case e1: Symbol if e1.isAliasType => - val underlying = e1.typeRef.underlyingClassRef(refinementOK = false).typeSymbol - if (underlying.name == e1.name) underlying else e1 - case _ => e1 - } - lazy val dealiased = followAlias(entry) - var alts = apply(str).dropWhile(alt => dealiased ne followAlias(alt)) - if (alts.isEmpty) { - alts = entry :: apply(str) - update(str, alts) - } - str + "'" * (alts.length - 1) - } - } - - private class ExplainingPrinter(seen: Seen)(_ctx: Context) extends RefinedPrinter(_ctx) { - override def simpleNameString(sym: Symbol): String = - if ((sym is ModuleClass) && sym.sourceModule.exists) simpleNameString(sym.sourceModule) - else seen.record(super.simpleNameString(sym), sym) - - override def polyParamNameString(param: PolyParam): String = - seen.record(super.polyParamNameString(param), param) - } - - /** Create explanation for single `Recorded` type or symbol */ - def explanation(entry: AnyRef)(implicit ctx: Context): String = { - def boundStr(bound: Type, default: ClassSymbol, cmp: String) = - if (bound.isRef(default)) "" else i"$cmp $bound" - - def boundsStr(bounds: TypeBounds): String = { - val lo = boundStr(bounds.lo, defn.NothingClass, ">:") - val hi = boundStr(bounds.hi, defn.AnyClass, "<:") - if (lo.isEmpty) hi - else if (hi.isEmpty) lo - else s"$lo and $hi" - } - - def addendum(cat: String, info: Type): String = info match { - case bounds @ TypeBounds(lo, hi) if bounds ne TypeBounds.empty => - if (lo eq hi) i" which is an alias of $lo" - else i" with $cat ${boundsStr(bounds)}" - case _ => - "" - } - - entry match { - case param: PolyParam => - s"is a type variable${addendum("constraint", ctx.typeComparer.bounds(param))}" - case sym: Symbol => - s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", sym.info)}" - } - } - - /** Turns a `Seen` into a `String` to produce an explanation for types on the - * form `where: T is...` - * - * @return string disambiguating types - */ - private def explanations(seen: Seen)(implicit ctx: Context): String = { - def needsExplanation(entry: Recorded) = entry match { - case param: PolyParam => ctx.typerState.constraint.contains(param) - case _ => false - } - - val toExplain: List[(String, Recorded)] = seen.toList.flatMap { - case (str, entry :: Nil) => - if (needsExplanation(entry)) (str, entry) :: Nil else Nil - case (str, entries) => - entries.map(alt => (seen.record(str, alt), alt)) - }.sortBy(_._1) - - def columnar(parts: List[(String, String)]): List[String] = { - lazy val maxLen = parts.map(_._1.length).max - parts.map { - case (leader, trailer) => - val variable = hl"$leader" - s"""$variable${" " * (maxLen - leader.length)} $trailer""" - } - } - - val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry)) } - val explainLines = columnar(explainParts) - if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n" - } - - /** Context with correct printer set for explanations */ - private def explainCtx(seen: Seen)(implicit ctx: Context): Context = ctx.printer match { - case dp: ExplainingPrinter => - ctx // re-use outer printer and defer explanation to it - case _ => ctx.fresh.setPrinterFn(ctx => new ExplainingPrinter(seen)(ctx)) - } - - /** Entrypoint for explanation string interpolator: - * - * ``` - * ex"disambiguate $tpe1 and $tpe2" - * ``` - */ - def explained2(op: Context => String)(implicit ctx: Context): String = { - val seen = new Seen - op(explainCtx(seen)) ++ explanations(seen) - } - - /** When getting a type mismatch it is useful to disambiguate placeholders like: - * - * ``` - * found: List[Int] - * required: List[T] - * where: T is a type in the initalizer of value s which is an alias of - * String - * ``` - * - * @return the `where` section as well as the printing context for the - * placeholders - `("T is a...", printCtx)` - */ - def disambiguateTypes(args: Type*)(implicit ctx: Context): (String, Context) = { - val seen = new Seen - val printCtx = explainCtx(seen) - args.foreach(_.show(printCtx)) // showing each member will put it into `seen` - (explanations(seen), printCtx) - } - - /** This method will produce a colored type diff from the given arguments. - * The idea is to do this for known cases that are useful and then fall back - * on regular syntax highlighting for the cases which are unhandled. - * - * Please not that if used in combination with `disambiguateTypes` the - * correct `Context` for printing should also be passed when calling the - * method. - * - * @return the (found, expected, changePercentage) with coloring to - * highlight the difference - */ - def typeDiff(found: Type, expected: Type)(implicit ctx: Context): (String, String) = { - val fnd = wrapNonSensical(found, found.show) - val exp = wrapNonSensical(expected, expected.show) - - DiffUtil.mkColoredTypeDiff(fnd, exp) match { - case _ if ctx.settings.color.value == "never" => (fnd, exp) - case (fnd, exp, change) if change < 0.5 => (fnd, exp) - case _ => (fnd, exp) - } - } -} diff --git a/src/dotty/tools/dotc/printing/Highlighting.scala b/src/dotty/tools/dotc/printing/Highlighting.scala deleted file mode 100644 index 3bda7fb7a..000000000 --- a/src/dotty/tools/dotc/printing/Highlighting.scala +++ /dev/null @@ -1,77 +0,0 @@ -package dotty.tools -package dotc -package printing - -import scala.collection.mutable -import core.Contexts.Context - -object Highlighting { - - implicit def highlightShow(h: Highlight)(implicit ctx: Context): String = - h.show - - abstract class Highlight(private val highlight: String) { - def text: String - - def show(implicit ctx: Context) = - if (ctx.settings.color.value == "never") text - else highlight + text + Console.RESET - - override def toString = - highlight + text + Console.RESET - - def +(other: Highlight)(implicit ctx: Context): HighlightBuffer = - new HighlightBuffer(this) + other - - def +(other: String)(implicit ctx: Context): HighlightBuffer = - new HighlightBuffer(this) + other - } - - abstract class Modifier(private val mod: String, text: String) extends Highlight(Console.RESET) { - override def show(implicit ctx: Context) = - if (ctx.settings.color.value == "never") "" - else mod + super.show - } - - case class HighlightBuffer(hl: Highlight)(implicit ctx: Context) { - val buffer = new mutable.ListBuffer[String] - - buffer += hl.show - - def +(other: Highlight): HighlightBuffer = { - buffer += other.show - this - } - - def +(other: String): HighlightBuffer = { - buffer += other - this - } - - override def toString = - buffer.mkString - } - - case class NoColor(text: String) extends Highlight(Console.RESET) - - case class Red(text: String) extends Highlight(Console.RED) - case class Blue(text: String) extends Highlight(Console.BLUE) - case class Cyan(text: String) extends Highlight(Console.CYAN) - case class Black(text: String) extends Highlight(Console.BLACK) - case class Green(text: String) extends Highlight(Console.GREEN) - case class White(text: String) extends Highlight(Console.WHITE) - case class Yellow(text: String) extends Highlight(Console.YELLOW) - case class Magenta(text: String) extends Highlight(Console.MAGENTA) - - case class RedB(text: String) extends Highlight(Console.RED_B) - case class BlueB(text: String) extends Highlight(Console.BLUE_B) - case class CyanB(text: String) extends Highlight(Console.CYAN_B) - case class BlackB(text: String) extends Highlight(Console.BLACK_B) - case class GreenB(text: String) extends Highlight(Console.GREEN_B) - case class WhiteB(text: String) extends Highlight(Console.WHITE_B) - case class YellowB(text: String) extends Highlight(Console.YELLOW_B) - case class MagentaB(text: String) extends Highlight(Console.MAGENTA_B) - - case class Bold(text: String) extends Modifier(Console.BOLD, text) - case class Underlined(text: String) extends Modifier(Console.UNDERLINED, text) -} diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala deleted file mode 100644 index 15c382bb0..000000000 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ /dev/null @@ -1,500 +0,0 @@ -package dotty.tools.dotc -package printing - -import core._ -import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._, Denotations._ -import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation -import StdNames.{nme, tpnme} -import ast.Trees._, ast._ -import config.Config -import java.lang.Integer.toOctalString -import config.Config.summarizeDepth -import scala.annotation.switch - -class PlainPrinter(_ctx: Context) extends Printer { - protected[this] implicit def ctx: Context = _ctx.addMode(Mode.Printing) - - private var openRecs: List[RecType] = Nil - - protected def maxToTextRecursions = 100 - - protected final def controlled(op: => Text): Text = - if (ctx.toTextRecursions < maxToTextRecursions && ctx.toTextRecursions < maxSummarized) - try { - ctx.toTextRecursions += 1 - op - } finally { - ctx.toTextRecursions -= 1 - } - else { - if (ctx.toTextRecursions >= maxToTextRecursions) - recursionLimitExceeded() - "..." - } - - protected def recursionLimitExceeded() = { - ctx.warning("Exceeded recursion depth attempting to print.") - if (ctx.debug) Thread.dumpStack() - } - - /** If true, tweak output so it is the same before and after pickling */ - protected def homogenizedView: Boolean = ctx.settings.YtestPickler.value - - def homogenize(tp: Type): Type = - if (homogenizedView) - tp match { - case tp: ThisType if tp.cls.is(Package) && !tp.cls.isEffectiveRoot => - ctx.requiredPackage(tp.cls.fullName).termRef - case tp: TypeVar if tp.isInstantiated => - homogenize(tp.instanceOpt) - case AndType(tp1, tp2) => - homogenize(tp1) & homogenize(tp2) - case OrType(tp1, tp2) => - homogenize(tp1) | homogenize(tp2) - case tp: SkolemType => - homogenize(tp.info) - case tp: LazyRef => - homogenize(tp.ref) - case HKApply(tycon, args) => - tycon.dealias.appliedTo(args) - case _ => - tp - } - else tp - - private def selfRecName(n: Int) = s"z$n" - - /** Render elements alternating with `sep` string */ - protected def toText(elems: Traversable[Showable], sep: String) = - Text(elems map (_ toText this), sep) - - /** Render element within highest precedence */ - protected def toTextLocal(elem: Showable): Text = - atPrec(DotPrec) { elem.toText(this) } - - /** Render element within lowest precedence */ - protected def toTextGlobal(elem: Showable): Text = - atPrec(GlobalPrec) { elem.toText(this) } - - protected def toTextLocal(elems: Traversable[Showable], sep: String) = - atPrec(DotPrec) { toText(elems, sep) } - - protected def toTextGlobal(elems: Traversable[Showable], sep: String) = - atPrec(GlobalPrec) { toText(elems, sep) } - - /** If the name of the symbol's owner should be used when you care about - * seeing an interesting name: in such cases this symbol is e.g. a method - * parameter with a synthetic name, a constructor named "this", an object - * "package", etc. The kind string, if non-empty, will be phrased relative - * to the name of the owner. - */ - protected def hasMeaninglessName(sym: Symbol) = ( - (sym is Param) && sym.owner.isSetter // x$1 - || sym.isClassConstructor // this - || (sym.name == nme.PACKAGE) // package - ) - - def nameString(name: Name): String = name.toString + { - if (ctx.settings.debugNames.value) - if (name.isTypeName) "/T" else "/V" - else "" - } - - def toText(name: Name): Text = Str(nameString(name)) - - /** String representation of a name used in a refinement - * In refined printing this undoes type parameter expansion - */ - protected def refinementNameString(tp: RefinedType) = nameString(tp.refinedName) - - /** String representation of a refinement */ - protected def toTextRefinement(rt: RefinedType) = - (refinementNameString(rt) ~ toTextRHS(rt.refinedInfo)).close - - protected def argText(arg: Type): Text = arg match { - case arg: TypeBounds => "_" ~ toTextGlobal(arg) - case _ => toTextGlobal(arg) - } - - /** The longest sequence of refinement types, starting at given type - * and following parents. - */ - private def refinementChain(tp: Type): List[Type] = - tp :: (tp match { - case tp: RefinedType => refinementChain(tp.parent.stripTypeVar) - case _ => Nil - }) - - def toText(tp: Type): Text = controlled { - homogenize(tp) match { - case tp: TypeType => - toTextRHS(tp) - case tp: TermRef - if !tp.denotationIsCurrent && !homogenizedView || // always print underyling when testing picklers - tp.symbol.is(Module) || - tp.symbol.name.isImportName => - toTextRef(tp) ~ ".type" - case tp: TermRef if tp.denot.isOverloaded => - "<overloaded " ~ toTextRef(tp) ~ ">" - case tp: SingletonType => - toTextLocal(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")" - case tp: TypeRef => - toTextPrefix(tp.prefix) ~ selectionString(tp) - case tp: RefinedType => - val parent :: (refined: List[RefinedType @unchecked]) = - refinementChain(tp).reverse - toTextLocal(parent) ~ "{" ~ Text(refined map toTextRefinement, "; ").close ~ "}" - case tp: RecType => - try { - openRecs = tp :: openRecs - "{" ~ selfRecName(openRecs.length) ~ " => " ~ toTextGlobal(tp.parent) ~ "}" - } - finally openRecs = openRecs.tail - case AndType(tp1, tp2) => - changePrec(AndPrec) { toText(tp1) ~ " & " ~ toText(tp2) } - case OrType(tp1, tp2) => - changePrec(OrPrec) { toText(tp1) ~ " | " ~ toText(tp2) } - case ErrorType => - "<error>" - case tp: WildcardType => - if (tp.optBounds.exists) "(?" ~ toTextRHS(tp.bounds) ~ ")" else "?" - case NoType => - "<notype>" - case NoPrefix => - "<noprefix>" - case tp: MethodType => - def paramText(name: TermName, tp: Type) = toText(name) ~ ": " ~ toText(tp) - changePrec(GlobalPrec) { - (if (tp.isImplicit) "(implicit " else "(") ~ - Text((tp.paramNames, tp.paramTypes).zipped map paramText, ", ") ~ - ")" ~ toText(tp.resultType) - } - case tp: ExprType => - changePrec(GlobalPrec) { "=> " ~ toText(tp.resultType) } - case tp: PolyType => - def paramText(variance: Int, name: Name, bounds: TypeBounds): Text = - varianceString(variance) ~ name.toString ~ toText(bounds) - changePrec(GlobalPrec) { - "[" ~ Text((tp.variances, tp.paramNames, tp.paramBounds).zipped.map(paramText), ", ") ~ - "] => " ~ toTextGlobal(tp.resultType) - } - case tp: PolyParam => - polyParamNameString(tp) ~ polyHash(tp.binder) - case AnnotatedType(tpe, annot) => - toTextLocal(tpe) ~ " " ~ toText(annot) - case HKApply(tycon, args) => - toTextLocal(tycon) ~ "[" ~ Text(args.map(argText), ", ") ~ "]" - case tp: TypeVar => - if (tp.isInstantiated) - toTextLocal(tp.instanceOpt) ~ "^" // debug for now, so that we can see where the TypeVars are. - else { - val constr = ctx.typerState.constraint - val bounds = - if (constr.contains(tp)) constr.fullBounds(tp.origin)(ctx.addMode(Mode.Printing)) - else TypeBounds.empty - if (ctx.settings.YshowVarBounds.value) "(" ~ toText(tp.origin) ~ "?" ~ toText(bounds) ~ ")" - else toText(tp.origin) - } - case tp: LazyRef => - "LazyRef(" ~ toTextGlobal(tp.ref) ~ ")" // TODO: only print this during debug mode? - case _ => - tp.fallbackToText(this) - } - }.close - - protected def polyParamNameString(name: TypeName): String = name.toString - - protected def polyParamNameString(param: PolyParam): String = polyParamNameString(param.binder.paramNames(param.paramNum)) - - /** The name of the symbol without a unique id. Under refined printing, - * the decoded original name. - */ - protected def simpleNameString(sym: Symbol): String = nameString(sym.name) - - /** If -uniqid is set, the hashcode of the polytype, after a # */ - protected def polyHash(pt: PolyType): Text = - if (ctx.settings.uniqid.value) "#" + pt.hashCode else "" - - /** If -uniqid is set, the unique id of symbol, after a # */ - protected def idString(sym: Symbol): String = - if (ctx.settings.uniqid.value) "#" + sym.id else "" - - def nameString(sym: Symbol): String = - simpleNameString(sym) + idString(sym) // + "<" + (if (sym.exists) sym.owner else "") + ">" - - def fullNameString(sym: Symbol): String = - if (sym.isRoot || sym == NoSymbol || sym.owner.isEffectiveRoot) - nameString(sym) - else - fullNameString(fullNameOwner(sym)) + "." + nameString(sym) - - protected def fullNameOwner(sym: Symbol): Symbol = sym.effectiveOwner.enclosingClass - - protected def objectPrefix = "object " - protected def packagePrefix = "package " - - protected def trimPrefix(text: Text) = - text.stripPrefix(objectPrefix).stripPrefix(packagePrefix) - - protected def selectionString(tp: NamedType) = { - val sym = if (homogenizedView) tp.symbol else tp.currentSymbol - if (sym.exists) nameString(sym) else nameString(tp.name) - } - - /** The string representation of this type used as a prefix */ - protected def toTextRef(tp: SingletonType): Text = controlled { - tp match { - case tp: TermRef => - toTextPrefix(tp.prefix) ~ selectionString(tp) - case tp: ThisType => - nameString(tp.cls) + ".this" - case SuperType(thistpe: SingletonType, _) => - toTextRef(thistpe).map(_.replaceAll("""\bthis$""", "super")) - case SuperType(thistpe, _) => - "Super(" ~ toTextGlobal(thistpe) ~ ")" - case tp @ ConstantType(value) => - toText(value) - case MethodParam(mt, idx) => - nameString(mt.paramNames(idx)) - case tp: RecThis => - val idx = openRecs.reverse.indexOf(tp.binder) - if (idx >= 0) selfRecName(idx + 1) - else "{...}.this" // TODO move underlying type to an addendum, e.g. ... z3 ... where z3: ... - case tp: SkolemType => - if (homogenizedView) toText(tp.info) else tp.repr - } - } - - /** The string representation of this type used as a prefix */ - protected def toTextPrefix(tp: Type): Text = controlled { - homogenize(tp) match { - case NoPrefix => "" - case tp: SingletonType => toTextRef(tp) ~ "." - case tp => trimPrefix(toTextLocal(tp)) ~ "#" - } - } - - protected def isOmittablePrefix(sym: Symbol): Boolean = - defn.UnqualifiedOwnerTypes.exists(_.symbol == sym) || isEmptyPrefix(sym) - - protected def isEmptyPrefix(sym: Symbol): Boolean = - sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName - - /** String representation of a definition's type following its name, - * if symbol is completed, "?" otherwise. - */ - protected def toTextRHS(optType: Option[Type]): Text = optType match { - case Some(tp) => toTextRHS(tp) - case None => "?" - } - - /** String representation of a definition's type following its name */ - protected def toTextRHS(tp: Type): Text = controlled { - homogenize(tp) match { - case tp @ TypeBounds(lo, hi) => - if (lo eq hi) { - val eql = - if (tp.variance == 1) " =+ " - else if (tp.variance == -1) " =- " - else " = " - eql ~ toText(lo) - } - else - (if (lo isRef defn.NothingClass) Text() else " >: " ~ toText(lo)) ~ - (if (hi isRef defn.AnyClass) Text() else " <: " ~ toText(hi)) - case tp @ ClassInfo(pre, cls, cparents, decls, selfInfo) => - val preText = toTextLocal(pre) - val (tparams, otherDecls) = decls.toList partition treatAsTypeParam - val tparamsText = - if (tparams.isEmpty) Text() else ("[" ~ dclsText(tparams) ~ "]").close - val selfText: Text = selfInfo match { - case NoType => Text() - case sym: Symbol if !sym.isCompleted => "this: ? =>" - case _ => "this: " ~ atPrec(InfixPrec) { toText(tp.selfType) } ~ " =>" - } - val trueDecls = otherDecls.filterNot(treatAsTypeArg) - val declsText = - if (trueDecls.isEmpty || !ctx.settings.debug.value) Text() - else dclsText(trueDecls) - tparamsText ~ " extends " ~ toTextParents(tp.parents) ~ "{" ~ selfText ~ declsText ~ - "} at " ~ preText - case tp => - ": " ~ toTextGlobal(tp) - } - } - - protected def toTextParents(parents: List[Type]): Text = Text(parents.map(toTextLocal), " with ") - - protected def treatAsTypeParam(sym: Symbol): Boolean = false - protected def treatAsTypeArg(sym: Symbol): Boolean = false - - /** String representation of symbol's kind. */ - def kindString(sym: Symbol): String = { - val flags = sym.flagsUNSAFE - if (flags is PackageClass) "package class" - else if (flags is PackageVal) "package" - else if (sym.isPackageObject) - if (sym.isClass) "package object class" - else "package object" - else if (sym.isAnonymousClass) "anonymous class" - else if (flags is ModuleClass) "module class" - else if (flags is ModuleVal) "module" - else if (flags is ImplClass) "implementation class" - else if (flags is Trait) "trait" - else if (sym.isClass) "class" - else if (sym.isType) "type" - else if (sym.isGetter) "getter" - else if (sym.isSetter) "setter" - else if (flags is Lazy) "lazy value" - else if (flags is Mutable) "variable" - else if (sym.isClassConstructor && sym.isPrimaryConstructor) "primary constructor" - else if (sym.isClassConstructor) "constructor" - else if (sym.is(Method)) "method" - else if (sym.isTerm) "value" - else "" - } - - /** String representation of symbol's definition key word */ - protected def keyString(sym: Symbol): String = { - val flags = sym.flagsUNSAFE - if (flags is JavaTrait) "interface" - else if ((flags is Trait) && !(flags is ImplClass)) "trait" - else if (sym.isClass) "class" - else if (sym.isType) "type" - else if (flags is Mutable) "var" - else if (flags is Package) "package" - else if (flags is Module) "object" - else if (sym is Method) "def" - else if (sym.isTerm && (!(flags is Param))) "val" - else "" - } - - /** String representation of symbol's flags */ - protected def toTextFlags(sym: Symbol): Text = - Text(sym.flagsUNSAFE.flagStrings map stringToText, " ") - - /** String representation of symbol's variance or "" if not applicable */ - protected def varianceString(sym: Symbol): String = varianceString(sym.variance) - - protected def varianceString(v: Int): String = v match { - case -1 => "-" - case 1 => "+" - case _ => "" - } - - def annotsText(sym: Symbol): Text = Text(sym.annotations.map(toText)) - - def dclText(sym: Symbol): Text = dclTextWithInfo(sym, sym.unforcedInfo) - - def dclText(d: SingleDenotation): Text = dclTextWithInfo(d.symbol, Some(d.info)) - - private def dclTextWithInfo(sym: Symbol, info: Option[Type]): Text = - (toTextFlags(sym) ~~ keyString(sym) ~~ - (varianceString(sym) ~ nameString(sym)) ~ toTextRHS(info)).close - - def toText(sym: Symbol): Text = - (kindString(sym) ~~ { - if (sym.isAnonymousClass) toText(sym.info.parents, " with ") ~ "{...}" - else if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym) - else nameString(sym) - }).close - - def locationText(sym: Symbol): Text = - if (!sym.exists) "" - else { - val ownr = sym.effectiveOwner - if (ownr.isClass && !isEmptyPrefix(ownr)) " in " ~ toText(ownr) else Text() - } - - def locatedText(sym: Symbol): Text = - (toText(sym) ~ locationText(sym)).close - - def extendedLocationText(sym: Symbol): Text = - if (!sym.exists) "" - else { - def recur(ownr: Symbol, innerLocation: String): Text = { - def nextOuter(innerKind: String): Text = - recur(ownr.effectiveOwner, - if (!innerLocation.isEmpty) innerLocation - else s" in an anonymous $innerKind") - def showLocation(ownr: Symbol, where: String): Text = - innerLocation ~ " " ~ where ~ " " ~ toText(ownr) - if (ownr.isAnonymousClass) nextOuter("class") - else if (ownr.isAnonymousFunction) nextOuter("function") - else if (isEmptyPrefix(ownr)) "" - else if (ownr.isLocalDummy) showLocation(ownr.owner, "locally defined in") - else if (ownr.isTerm && !ownr.is(Module | Method)) showLocation(ownr, "in the initalizer of") - else showLocation(ownr, "in") - } - recur(sym.owner, "") - } - - def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D" - - @switch private def escapedChar(ch: Char): String = ch match { - case '\b' => "\\b" - case '\t' => "\\t" - case '\n' => "\\n" - case '\f' => "\\f" - case '\r' => "\\r" - case '"' => "\\\"" - case '\'' => "\\\'" - case '\\' => "\\\\" - case _ => if (ch.isControl) "\\0" + toOctalString(ch) else String.valueOf(ch) - } - - def toText(const: Constant): Text = const.tag match { - case StringTag => "\"" + escapedString(const.value.toString) + "\"" - case ClazzTag => "classOf[" ~ toText(const.typeValue.classSymbol) ~ "]" - case CharTag => s"'${escapedChar(const.charValue)}'" - case LongTag => const.longValue.toString + "L" - case EnumTag => const.symbolValue.name.toString - case _ => String.valueOf(const.value) - } - - def toText(annot: Annotation): Text = s"@${annot.symbol.name}" // for now - - protected def escapedString(str: String): String = str flatMap escapedChar - - def dclsText(syms: List[Symbol], sep: String): Text = Text(syms map dclText, sep) - - def toText(sc: Scope): Text = - ("Scope{" ~ dclsText(sc.toList) ~ "}").close - - def toText[T >: Untyped](tree: Tree[T]): Text = { - tree match { - case node: Positioned => - def toTextElem(elem: Any): Text = elem match { - case elem: Showable => elem.toText(this) - case elem: List[_] => "List(" ~ Text(elem map toTextElem, ",") ~ ")" - case elem => elem.toString - } - val nodeName = node.productPrefix - val elems = - Text(node.productIterator.map(toTextElem).toList, ", ") - val tpSuffix = - if (ctx.settings.printtypes.value && tree.hasType) - " | " ~ toText(tree.typeOpt) - else - Text() - - nodeName ~ "(" ~ elems ~ tpSuffix ~ ")" ~ node.pos.toString - case _ => - tree.fallbackToText(this) - } - }.close // todo: override in refined printer - - private var maxSummarized = Int.MaxValue - - def summarized[T](depth: Int)(op: => T): T = { - val saved = maxSummarized - maxSummarized = ctx.toTextRecursions + depth - try op - finally maxSummarized = depth - } - - def summarized[T](op: => T): T = summarized(summarizeDepth)(op) - - def plain = this -} - diff --git a/src/dotty/tools/dotc/printing/Printer.scala b/src/dotty/tools/dotc/printing/Printer.scala deleted file mode 100644 index 14b63012e..000000000 --- a/src/dotty/tools/dotc/printing/Printer.scala +++ /dev/null @@ -1,105 +0,0 @@ -package dotty.tools.dotc -package printing - -import core._ -import Texts._, ast.Trees._ -import Types.Type, Symbols.Symbol, Contexts.Context, Scopes.Scope, Constants.Constant, - Names.Name, Denotations._, Annotations.Annotation - -/** The base class of all printers - */ -abstract class Printer { - - private[this] var prec: Precedence = GlobalPrec - - /** The current precedence level */ - def currentPrecedence = prec - - /** Generate text using `op`, assuming a given precedence level `prec`. */ - def atPrec(prec: Precedence)(op: => Text): Text = { - val outerPrec = this.prec - this.prec = prec - try op - finally this.prec = outerPrec - } - - /** Generate text using `op`, assuming a given precedence level `prec`. - * If new level `prec` is lower than previous level, put text in parentheses. - */ - def changePrec(prec: Precedence)(op: => Text): Text = - if (prec < this.prec) atPrec(prec) ("(" ~ op ~ ")") else atPrec(prec)(op) - - /** The name, possibley with with namespace suffix if debugNames is set: - * /L for local names, /V for other term names, /T for type names - */ - def nameString(name: Name): String - - /** The name of the given symbol. - * If !settings.debug, the original name where - * expansions of operators are translated back to operator symbol. - * E.g. $eq => =. - * If settings.uniqid, adds id. - */ - def nameString(sym: Symbol): String - - /** The fully qualified name of the symbol */ - def fullNameString(sym: Symbol): String - - /** The kind of the symbol */ - def kindString(sym: Symbol): String - - /** The name as a text */ - def toText(name: Name): Text - - /** Textual representation, including symbol's kind e.g., "class Foo", "method Bar". - * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. - */ - def toText(sym: Symbol): Text - - /** Textual representation of symbol's declaration */ - def dclText(sym: Symbol): Text - - /** Textual representation of single denotation's declaration */ - def dclText(sd: SingleDenotation): Text - - /** If symbol's owner is a printable class C, the text "in C", otherwise "" */ - def locationText(sym: Symbol): Text - - /** Textual representation of symbol and its location */ - def locatedText(sym: Symbol): Text - - /** A description of sym's location */ - def extendedLocationText(sym: Symbol): Text - - /** Textual representation of denotation */ - def toText(denot: Denotation): Text - - /** Textual representation of constant */ - def toText(const: Constant): Text - - /** Textual representation of annotation */ - def toText(annot: Annotation): Text - - /** Textual representation of type */ - def toText(tp: Type): Text - - /** Textual representation of all symbols in given list, - * using `dclText` for displaying each. - */ - def dclsText(syms: List[Symbol], sep: String = "\n"): Text - - /** Textual representation of all definitions in a scope using `dclText` for each */ - def toText(sc: Scope): Text - - /** Textual representation of tree */ - def toText[T >: Untyped](tree: Tree[T]): Text - - /** Perform string or text-producing operation `op` so that only a - * summarized text with given recursion depth is shown - */ - def summarized[T](depth: Int)(op: => T): T - - /** A plain printer without any embellishments */ - def plain: Printer -} - diff --git a/src/dotty/tools/dotc/printing/Printers.scala b/src/dotty/tools/dotc/printing/Printers.scala deleted file mode 100644 index 36043a4ff..000000000 --- a/src/dotty/tools/dotc/printing/Printers.scala +++ /dev/null @@ -1,14 +0,0 @@ -package dotty.tools.dotc -package printing - -import core.Contexts.Context - -trait Printers { this: Context => - - /** A function creating a printer */ - def printer = { - val pr = printerFn(this) - if (this.settings.YplainPrinter.value) pr.plain else pr - } -} - diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala deleted file mode 100644 index 29e1d4869..000000000 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ /dev/null @@ -1,652 +0,0 @@ -package dotty.tools.dotc -package printing - -import core._ -import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._ -import TypeErasure.ErasedValueType -import Contexts.Context, Scopes.Scope, Denotations._, SymDenotations._, Annotations.Annotation -import StdNames.{nme, tpnme} -import ast.{Trees, untpd, tpd} -import typer.{Namer, Inliner} -import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto, dummyTreeOfType} -import Trees._ -import TypeApplications._ -import Decorators._ -import config.Config -import scala.annotation.switch -import language.implicitConversions - -class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { - - /** A stack of enclosing DefDef, TypeDef, or ClassDef, or ModuleDefs nodes */ - private var enclosingDef: untpd.Tree = untpd.EmptyTree - private var myCtx: Context = _ctx - private var printPos = ctx.settings.Yprintpos.value - override protected[this] implicit def ctx: Context = myCtx - - def withEnclosingDef(enclDef: Tree[_ >: Untyped])(op: => Text): Text = { - val savedCtx = myCtx - if (enclDef.hasType && enclDef.symbol.exists) - myCtx = ctx.withOwner(enclDef.symbol) - val savedDef = enclosingDef - enclosingDef = enclDef - try op finally { - myCtx = savedCtx - enclosingDef = savedDef - } - } - - def inPattern(op: => Text): Text = { - val savedCtx = myCtx - myCtx = ctx.addMode(Mode.Pattern) - try op finally myCtx = savedCtx - } - - def withoutPos(op: => Text): Text = { - val savedPrintPos = printPos - printPos = false - try op finally printPos = savedPrintPos - } - - private def enclDefIsClass = enclosingDef match { - case owner: TypeDef[_] => owner.isClassDef - case owner: untpd.ModuleDef => true - case _ => false - } - - override protected def recursionLimitExceeded() = {} - - protected val PrintableFlags = (SourceModifierFlags | Label | Module | Local).toCommonFlags - - override def nameString(name: Name): String = name.decode.toString - - override protected def simpleNameString(sym: Symbol): String = { - val name = sym.originalName - nameString(if (sym is ExpandedTypeParam) name.asTypeName.unexpandedName else name) - } - - override def fullNameString(sym: Symbol): String = - if (isEmptyPrefix(sym.maybeOwner)) nameString(sym) - else super.fullNameString(sym) - - override protected def fullNameOwner(sym: Symbol) = { - val owner = super.fullNameOwner(sym) - if (owner is ModuleClass) owner.sourceModule else owner - } - - override def toTextRef(tp: SingletonType): Text = controlled { - tp match { - case tp: ThisType => - if (tp.cls.isAnonymousClass) return "this" - if (tp.cls is ModuleClass) return fullNameString(tp.cls.sourceModule) - case _ => - } - super.toTextRef(tp) - } - - override def toTextPrefix(tp: Type): Text = controlled { - def isOmittable(sym: Symbol) = - if (ctx.settings.verbose.value) false - else if (homogenizedView) isEmptyPrefix(sym) // drop <root> and anonymous classes, but not scala, Predef. - else isOmittablePrefix(sym) - tp match { - case tp: ThisType => - if (isOmittable(tp.cls)) return "" - case tp @ TermRef(pre, _) => - val sym = tp.symbol - if (sym.isPackageObject) return toTextPrefix(pre) - if (isOmittable(sym)) return "" - case _ => - } - super.toTextPrefix(tp) - } - - override protected def refinementNameString(tp: RefinedType): String = - if (tp.parent.isInstanceOf[WildcardType] || tp.refinedName == nme.WILDCARD) - super.refinementNameString(tp) - else { - val tsym = tp.parent.member(tp.refinedName).symbol - if (!tsym.exists) super.refinementNameString(tp) - else simpleNameString(tsym) - } - - override def toText(tp: Type): Text = controlled { - def toTextTuple(args: List[Type]): Text = - "(" ~ Text(args.map(argText), ", ") ~ ")" - def toTextFunction(args: List[Type]): Text = - changePrec(GlobalPrec) { - val argStr: Text = - if (args.length == 2 && !defn.isTupleType(args.head)) - atPrec(InfixPrec) { argText(args.head) } - else - toTextTuple(args.init) - argStr ~ " => " ~ argText(args.last) - } - homogenize(tp) match { - case AppliedType(tycon, args) => - val cls = tycon.typeSymbol - if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" - if (defn.isFunctionClass(cls)) return toTextFunction(args) - if (defn.isTupleClass(cls)) return toTextTuple(args) - return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close - case tp: TypeRef => - val hideType = tp.symbol is AliasPreferred - if (hideType && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) { - tp.info match { - case TypeAlias(alias) => return toText(alias) - case _ => if (tp.prefix.isInstanceOf[ThisType]) return nameString(tp.symbol) - } - } - else if (tp.symbol.isAnonymousClass && !ctx.settings.uniqid.value) - return toText(tp.info) - case ExprType(result) => - return "=> " ~ toText(result) - case ErasedValueType(tycon, underlying) => - return "ErasedValueType(" ~ toText(tycon) ~ ", " ~ toText(underlying) ~ ")" - case tp: ClassInfo => - return toTextParents(tp.parentsWithArgs) ~ "{...}" - case JavaArrayType(elemtp) => - return toText(elemtp) ~ "[]" - case tp: AnnotatedType if homogenizedView => - // Positions of annotations in types are not serialized - // (they don't need to because we keep the original type tree with - // the original annotation anyway. Therefore, there will always be - // one version of the annotation tree that has the correct positions). - withoutPos(super.toText(tp)) - case tp: SelectionProto => - return "?{ " ~ toText(tp.name) ~ (" " provided !tp.name.decode.last.isLetterOrDigit) ~ - ": " ~ toText(tp.memberProto) ~ " }" - case tp: ViewProto => - return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType) - case tp @ FunProto(args, resultType, _) => - val argsText = args match { - case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp) - case _ => toTextGlobal(args, ", ") - } - return "FunProto(" ~ argsText ~ "):" ~ toText(resultType) - case tp: IgnoredProto => - return "?" - case _ => - } - super.toText(tp) - } - - def blockText[T >: Untyped](trees: List[Tree[T]]): Text = - ("{" ~ toText(trees, "\n") ~ "}").close - - override def toText[T >: Untyped](tree: Tree[T]): Text = controlled { - - import untpd.{modsDeco => _, _} - - /** Print modifiers from symbols if tree has type, overriding the untpd behavior. */ - implicit def modsDeco(mdef: untpd.MemberDef)(implicit ctx: Context): untpd.ModsDecorator = - new untpd.ModsDecorator { - def mods = if (mdef.hasType) Modifiers(mdef.symbol) else mdef.rawMods - } - - def Modifiers(sym: Symbol)(implicit ctx: Context): Modifiers = untpd.Modifiers( - sym.flags & (if (sym.isType) ModifierFlags | VarianceFlags else ModifierFlags), - if (sym.privateWithin.exists) sym.privateWithin.asType.name else tpnme.EMPTY, - sym.annotations map (_.tree)) - - def isLocalThis(tree: Tree) = tree.typeOpt match { - case tp: ThisType => tp.cls == ctx.owner.enclosingClass - case _ => false - } - - def optDotPrefix(tree: This) = optText(tree.qual)(_ ~ ".") provided !isLocalThis(tree) - - def optAscription(tpt: untpd.Tree) = optText(tpt)(": " ~ _) - // Dotty deviation: called with an untpd.Tree, so cannot be a untpd.Tree[T] (seems to be a Scala2 problem to allow this) - // More deviations marked below as // DD - - def tparamsText[T >: Untyped](params: List[Tree]): Text = - "[" ~ toText(params, ", ") ~ "]" provided params.nonEmpty - - def addVparamssText(txt: Text, vparamss: List[List[ValDef]]): Text = - (txt /: vparamss)((txt, vparams) => txt ~ "(" ~ toText(vparams, ", ") ~ ")") - - def caseBlockText(tree: Tree): Text = tree match { - case Block(stats, expr) => toText(stats :+ expr, "\n") - case expr => toText(expr) - } - - def enumText(tree: untpd.Tree) = tree match { // DD - case _: untpd.GenFrom | _: untpd.GenAlias => toText(tree) - case _ => "if " ~ toText(tree) - } - - def forText(enums: List[untpd.Tree], expr: untpd.Tree, sep: String): Text = // DD - changePrec(GlobalPrec) { "for " ~ Text(enums map enumText, "; ") ~ sep ~ toText(expr) } - - def cxBoundToText(bound: untpd.Tree): Text = bound match { // DD - case AppliedTypeTree(tpt, _) => " : " ~ toText(tpt) - case untpd.Function(_, tpt) => " <% " ~ toText(tpt) - } - - def constrText(tree: untpd.Tree): Text = toTextLocal(tree).stripPrefix("new ") // DD - - def annotText(tree: untpd.Tree): Text = "@" ~ constrText(tree) // DD - - def useSymbol = - tree.hasType && tree.symbol.exists && ctx.settings.YprintSyms.value - - def modText(mods: untpd.Modifiers, kw: String): Text = { // DD - val suppressKw = if (enclDefIsClass) mods is ParamAndLocal else mods is Param - var flagMask = - if (ctx.settings.debugFlags.value) AnyFlags - else if (suppressKw) PrintableFlags &~ Private - else PrintableFlags - if (homogenizedView && mods.flags.isTypeFlags) flagMask &~= Implicit // drop implicit from classes - val flagsText = (mods.flags & flagMask).toString - Text(mods.annotations.map(annotText), " ") ~~ flagsText ~~ (kw provided !suppressKw) - } - - def varianceText(mods: untpd.Modifiers) = - if (mods is Covariant) "+" - else if (mods is Contravariant) "-" - else "" - - def argText(arg: Tree): Text = arg match { - case arg: TypeBoundsTree => "_" ~ toTextGlobal(arg) - case arg: TypeTree => - arg.typeOpt match { - case tp: TypeBounds => "_" ~ toTextGlobal(arg) - case _ => toTextGlobal(arg) - } - case _ => toTextGlobal(arg) - } - - def dclTextOr(treeText: => Text) = - if (useSymbol) - annotsText(tree.symbol) ~~ dclText(tree.symbol) ~ - ( " <in " ~ toText(tree.symbol.owner) ~ ">" provided ctx.settings.debugOwners.value) - else treeText - - def idText(tree: untpd.Tree): Text = { - if (ctx.settings.uniqid.value && tree.hasType && tree.symbol.exists) s"#${tree.symbol.id}" else "" - } - - def nameIdText(tree: untpd.NameTree): Text = - if (tree.hasType && tree.symbol.exists) nameString(tree.symbol) - else toText(tree.name) ~ idText(tree) - - def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { - val Template(constr @ DefDef(_, tparams, vparamss, _, _), parents, self, _) = impl - val tparamsTxt = withEnclosingDef(constr) { tparamsText(tparams) } - val primaryConstrs = if (constr.rhs.isEmpty) Nil else constr :: Nil - val prefix: Text = - if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt - else { - var modsText = modText(constr.mods, "") - if (!modsText.isEmpty) modsText = " " ~ modsText - if (constr.mods.hasAnnotations && !constr.mods.hasFlags) modsText = modsText ~~ " this" - withEnclosingDef(constr) { addVparamssText(tparamsTxt ~~ modsText, vparamss) } - } - val parentsText = Text(parents map constrText, " with ") - val selfText = { - val selfName = if (self.name == nme.WILDCARD) "this" else self.name.toString - (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close - } provided !self.isEmpty - val bodyText = "{" ~~ selfText ~~ toTextGlobal(primaryConstrs ::: impl.body, "\n") ~ "}" - prefix ~ (" extends" provided !ofNew) ~~ parentsText ~~ bodyText - } - - def toTextPackageId(pid: Tree): Text = - if (homogenizedView && pid.hasType) toTextLocal(pid.tpe) - else toTextLocal(pid) - - def toTextCore(tree: Tree): Text = tree match { - case id: Trees.BackquotedIdent[_] if !homogenizedView => - "`" ~ toText(id.name) ~ "`" - case Ident(name) => - tree.typeOpt match { - case tp: NamedType if name != nme.WILDCARD => - val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix - toTextPrefix(pre) ~ selectionString(tp) - case _ => - toText(name) - } - case tree @ Select(qual, name) => - if (qual.isType) toTextLocal(qual) ~ "#" ~ toText(name) - else toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided name != nme.CONSTRUCTOR) - case tree: This => - optDotPrefix(tree) ~ "this" ~ idText(tree) - case Super(qual: This, mix) => - optDotPrefix(qual) ~ "super" ~ optText(mix)("[" ~ _ ~ "]") - case Apply(fun, args) => - if (fun.hasType && fun.symbol == defn.throwMethod) - changePrec (GlobalPrec) { - "throw " ~ toText(args.head) - } - else - toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")" - case TypeApply(fun, args) => - toTextLocal(fun) ~ "[" ~ toTextGlobal(args, ", ") ~ "]" - case Literal(c) => - tree.typeOpt match { - case ConstantType(tc) => toText(tc) - case _ => toText(c) - } - case New(tpt) => - "new " ~ { - tpt match { - case tpt: Template => toTextTemplate(tpt, ofNew = true) - case _ => - if (tpt.hasType) - toTextLocal(tpt.typeOpt.underlyingClassRef(refinementOK = false)) - else - toTextLocal(tpt) - } - } - case Typed(expr, tpt) => - changePrec(InfixPrec) { toText(expr) ~ ": " ~ toText(tpt) } - case NamedArg(name, arg) => - toText(name) ~ " = " ~ toText(arg) - case Assign(lhs, rhs) => - changePrec(GlobalPrec) { toTextLocal(lhs) ~ " = " ~ toText(rhs) } - case Block(stats, expr) => - blockText(stats :+ expr) - case If(cond, thenp, elsep) => - changePrec(GlobalPrec) { - "if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _) - } - case Closure(env, ref, target) => - "closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~ - toTextGlobal(ref) ~ (":" ~ toText(target) provided !target.isEmpty) ~ ")" - case Match(sel, cases) => - if (sel.isEmpty) blockText(cases) - else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) } - case CaseDef(pat, guard, body) => - "case " ~ inPattern(toText(pat)) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body) - case Return(expr, from) => - changePrec(GlobalPrec) { "return" ~ optText(expr)(" " ~ _) } - case Try(expr, cases, finalizer) => - changePrec(GlobalPrec) { - "try " ~ toText(expr) ~ optText(cases)(" catch " ~ _) ~ optText(finalizer)(" finally " ~ _) - } - case Throw(expr) => - changePrec(GlobalPrec) { - "throw " ~ toText(expr) - } - case SeqLiteral(elems, elemtpt) => - "[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]" - case tree @ Inlined(call, bindings, body) => - (("/* inlined from " ~ toText(call) ~ "*/ ") provided !homogenizedView) ~ - blockText(bindings :+ body) - case tpt: untpd.DerivedTypeTree => - "<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">" - case TypeTree() => - toText(tree.typeOpt) - case SingletonTypeTree(ref) => - toTextLocal(ref) ~ ".type" - case AndTypeTree(l, r) => - changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) } - case OrTypeTree(l, r) => - changePrec(OrPrec) { toText(l) ~ " | " ~ toText(r) } - case RefinedTypeTree(tpt, refines) => - toTextLocal(tpt) ~ " " ~ blockText(refines) - case AppliedTypeTree(tpt, args) => - toTextLocal(tpt) ~ "[" ~ Text(args map argText, ", ") ~ "]" - case PolyTypeTree(tparams, body) => - changePrec(GlobalPrec) { - tparamsText(tparams) ~ " -> " ~ toText(body) - } - case ByNameTypeTree(tpt) => - "=> " ~ toTextLocal(tpt) - case TypeBoundsTree(lo, hi) => - optText(lo)(" >: " ~ _) ~ optText(hi)(" <: " ~ _) - case Bind(name, body) => - changePrec(InfixPrec) { toText(name) ~ " @ " ~ toText(body) } - case Alternative(trees) => - changePrec(OrPrec) { toText(trees, " | ") } - case UnApply(fun, implicits, patterns) => - val extractor = fun match { - case Select(extractor, nme.unapply) => extractor - case _ => fun - } - toTextLocal(extractor) ~ - "(" ~ toTextGlobal(patterns, ", ") ~ ")" ~ - ("(" ~ toTextGlobal(implicits, ", ") ~ ")" provided implicits.nonEmpty) - case tree @ ValDef(name, tpt, _) => - dclTextOr { - modText(tree.mods, if (tree.mods is Mutable) "var" else "val") ~~ - nameIdText(tree) ~ optAscription(tpt) ~ - withEnclosingDef(tree) { optText(tree.rhs)(" = " ~ _) } - } - case tree @ DefDef(name, tparams, vparamss, tpt, _) => - dclTextOr { - val prefix = modText(tree.mods, "def") ~~ nameIdText(tree) - withEnclosingDef(tree) { - addVparamssText(prefix ~ tparamsText(tparams), vparamss) ~ optAscription(tpt) ~ - optText(tree.rhs)(" = " ~ _) - } - } - case tree @ TypeDef(name, rhs) => - def typeDefText(tparamsText: => Text, rhsText: => Text) = - dclTextOr { - modText(tree.mods, "type") ~~ (varianceText(tree.mods) ~ nameIdText(tree)) ~ - withEnclosingDef(tree) { - if (tree.hasType) toText(tree.symbol.info) // TODO: always print RHS, once we pickle/unpickle type trees - else tparamsText ~ rhsText - } - } - def recur(rhs: Tree, tparamsTxt: => Text): Text = rhs match { - case impl: Template => - modText(tree.mods, if ((tree).mods is Trait) "trait" else "class") ~~ - nameIdText(tree) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~ - (if (tree.hasType && ctx.settings.verbose.value) i"[decls = ${tree.symbol.info.decls}]" else "") - case rhs: TypeBoundsTree => - typeDefText(tparamsTxt, toText(rhs)) - case PolyTypeTree(tparams, body) => - recur(body, tparamsText(tparams)) - case rhs => - typeDefText(tparamsTxt, optText(rhs)(" = " ~ _)) - } - recur(rhs, "") - case Import(expr, selectors) => - def selectorText(sel: Tree): Text = sel match { - case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r) - case _ => toTextGlobal(sel) - } - val selectorsText: Text = selectors match { - case id :: Nil => toText(id) - case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}" - } - "import " ~ toTextLocal(expr) ~ "." ~ selectorsText - case PackageDef(pid, stats) => - val statsText = stats match { - case (pdef: PackageDef) :: Nil => toText(pdef) - case _ => toTextGlobal(stats, "\n") - } - val bodyText = - if (currentPrecedence == TopLevelPrec) "\n" ~ statsText else " {" ~ statsText ~ "}" - "package " ~ toTextPackageId(pid) ~ bodyText - case tree: Template => - toTextTemplate(tree) - case Annotated(arg, annot) => - toTextLocal(arg) ~~ annotText(annot) - case EmptyTree => - "<empty>" - case TypedSplice(t) => - toText(t) - case tree @ ModuleDef(name, impl) => - withEnclosingDef(tree) { - modText(tree.mods, "object") ~~ nameIdText(tree) ~ toTextTemplate(impl) - } - case SymbolLit(str) => - "'" + str - case InterpolatedString(id, segments) => - def strText(str: Literal) = Str(escapedString(str.const.stringValue)) - def segmentText(segment: Tree) = segment match { - case Thicket(List(str: Literal, expr)) => strText(str) ~ "{" ~ toTextGlobal(expr) ~ "}" - case str: Literal => strText(str) - } - toText(id) ~ "\"" ~ Text(segments map segmentText, "") ~ "\"" - case Function(args, body) => - var implicitSeen: Boolean = false - def argToText(arg: Tree) = arg match { - case arg @ ValDef(name, tpt, _) => - val implicitText = - if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; "implicit " } - else "" - implicitText ~ toText(name) ~ optAscription(tpt) - case _ => - toText(arg) - } - val argsText = args match { - case (arg @ ValDef(_, tpt, _)) :: Nil if tpt.isEmpty => argToText(arg) - case _ => "(" ~ Text(args map argToText, ", ") ~ ")" - } - changePrec(GlobalPrec) { - argsText ~ " => " ~ toText(body) - } - case InfixOp(l, op, r) => - val opPrec = parsing.precedence(op) - changePrec(opPrec) { toText(l) ~ " " ~ toText(op) ~ " " ~ toText(r) } - case PostfixOp(l, op) => - changePrec(InfixPrec) { toText(l) ~ " " ~ toText(op) } - case PrefixOp(op, r) => - changePrec(DotPrec) { toText(op) ~ " " ~ toText(r) } - case Parens(t) => - "(" ~ toTextGlobal(t) ~ ")" - case Tuple(ts) => - "(" ~ toTextGlobal(ts, ", ") ~ ")" - case WhileDo(cond, body) => - changePrec(GlobalPrec) { "while " ~ toText(cond) ~ " do " ~ toText(body) } - case DoWhile(cond, body) => - changePrec(GlobalPrec) { "do " ~ toText(body) ~ " while " ~ toText(cond) } - case ForYield(enums, expr) => - forText(enums, expr, " yield ") - case ForDo(enums, expr) => - forText(enums, expr, " do ") - case GenFrom(pat, expr) => - toText(pat) ~ " <- " ~ toText(expr) - case GenAlias(pat, expr) => - toText(pat) ~ " = " ~ toText(expr) - case ContextBounds(bounds, cxBounds) => - (toText(bounds) /: cxBounds) {(t, cxb) => - t ~ cxBoundToText(cxb) - } - case PatDef(mods, pats, tpt, rhs) => - modText(mods, "val") ~~ toText(pats, ", ") ~ optAscription(tpt) ~ - optText(rhs)(" = " ~ _) - case ParsedTry(expr, handler, finalizer) => - changePrec(GlobalPrec) { - "try " ~ toText(expr) ~ " catch {" ~ toText(handler) ~ "}" ~ optText(finalizer)(" finally " ~ _) - } - case Thicket(trees) => - "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" - case _ => - tree.fallbackToText(this) - } - - var txt = toTextCore(tree) - - def suppressTypes = - tree.isType || tree.isDef || // don't print types of types or defs - homogenizedView && ctx.mode.is(Mode.Pattern) - // When comparing pickled info, disregard types of patterns. - // The reason is that GADT matching can rewrite types of pattern trees - // without changing the trees themselves. (see Typer.typedCase.indexPatterns.transform). - // But then pickling and unpickling the original trees will yield trees - // with the original types before they are rewritten, which causes a discrepancy. - - def suppressPositions = tree match { - case _: WithoutTypeOrPos[_] | _: TypeTree => true // TypeTrees never have an interesting position - case _ => false - } - - if (ctx.settings.printtypes.value && tree.hasType) { - // add type to term nodes; replace type nodes with their types unless -Yprintpos is also set. - def tp = tree.typeOpt match { - case tp: TermRef if tree.isInstanceOf[RefTree] && !tp.denot.isOverloaded => tp.underlying - case tp => tp - } - if (!suppressTypes) - txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close - else if (tree.isType && !homogenizedView) - txt = toText(tp) - } - if (printPos && !suppressPositions) { - // add positions - val pos = - if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic - else tree.pos - val clsStr = ""//if (tree.isType) tree.getClass.toString else "" - txt = (txt ~ "@" ~ pos.toString ~ clsStr).close - } - tree match { - case Block(_, _) | Template(_, _, _, _) => txt - case _ => txt.close - } - } - - def optText(name: Name)(encl: Text => Text): Text = - if (name.isEmpty) "" else encl(toText(name)) - - def optText[T >: Untyped](tree: Tree[T])(encl: Text => Text): Text = - if (tree.isEmpty) "" else encl(toText(tree)) - - def optText[T >: Untyped](tree: List[Tree[T]])(encl: Text => Text): Text = - if (tree.exists(!_.isEmpty)) encl(blockText(tree)) else "" - - override protected def polyParamNameString(name: TypeName): String = - name.unexpandedName.toString - - override protected def treatAsTypeParam(sym: Symbol): Boolean = sym is TypeParam - - override protected def treatAsTypeArg(sym: Symbol) = - sym.isType && (sym is ProtectedLocal) && - (sym.allOverriddenSymbols exists (_ is TypeParam)) - - override def toText(sym: Symbol): Text = { - if (sym.isImport) { - def importString(tree: untpd.Tree) = s"import ${tree.show}" - sym.infoOrCompleter match { - case info: Namer#Completer => return importString(info.original) - case info: ImportType => return importString(info.expr) - case _ => - } - } - if (sym.is(ModuleClass)) - kindString(sym) ~~ (nameString(sym.name.stripModuleClassSuffix) + idString(sym)) - else - super.toText(sym) - } - - override def kindString(sym: Symbol) = { - val flags = sym.flagsUNSAFE - if (flags is Package) "package" - else if (sym.isPackageObject) "package object" - else if (flags is Module) "object" - else if (flags is ImplClass) "class" - else if (sym.isClassConstructor) "constructor" - else super.kindString(sym) - } - - override protected def keyString(sym: Symbol): String = { - val flags = sym.flagsUNSAFE - if (sym.isType && sym.owner.isTerm) "" - else super.keyString(sym) - } - - override def toTextFlags(sym: Symbol) = - if (ctx.settings.debugFlags.value) - super.toTextFlags(sym) - else { - var flags = sym.flagsUNSAFE - if (flags is TypeParam) flags = flags &~ Protected - Text((flags & PrintableFlags).flagStrings map stringToText, " ") - } - - override def toText(denot: Denotation): Text = denot match { - case denot: MultiDenotation => Text(denot.alternatives.map(dclText), " <and> ") - case NoDenotation => "NoDenotation" - case _ => - if (denot.symbol.exists) toText(denot.symbol) - else "some " ~ toText(denot.info) - } - - override def plain = new PlainPrinter(_ctx) -} diff --git a/src/dotty/tools/dotc/printing/Showable.scala b/src/dotty/tools/dotc/printing/Showable.scala deleted file mode 100644 index efddb26f7..000000000 --- a/src/dotty/tools/dotc/printing/Showable.scala +++ /dev/null @@ -1,34 +0,0 @@ -package dotty.tools.dotc -package printing - -import core._ - -import Contexts._, Texts._, Decorators._ -import config.Config.summarizeDepth -import scala.util.control.NonFatal - -trait Showable extends Any { - - /** The text representation of this showable element. - * This normally dispatches to a pattern matching - * method in Printers. - */ - def toText(printer: Printer): Text - - /** A fallback text representation, if the pattern matching - * in Printers does not have a case for this showable element - */ - def fallbackToText(printer: Printer): Text = toString - - /** The string representation of this showable element. */ - def show(implicit ctx: Context): String = toText(ctx.printer).show - - /** The summarized string representation of this showable element. - * Recursion depth is limited to some smallish value. Default is - * Config.summarizeDepth. - */ - def showSummary(depth: Int)(implicit ctx: Context): String = - ctx.printer.summarized(depth)(show) - - def showSummary(implicit ctx: Context): String = showSummary(summarizeDepth) -} diff --git a/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala deleted file mode 100644 index 86f34e64d..000000000 --- a/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala +++ /dev/null @@ -1,304 +0,0 @@ -package dotty.tools -package dotc -package printing - -import parsing.Tokens._ -import scala.annotation.switch -import scala.collection.mutable.StringBuilder -import core.Contexts.Context -import Highlighting.{Highlight, HighlightBuffer} - -/** This object provides functions for syntax highlighting in the REPL */ -object SyntaxHighlighting { - - val NoColor = Console.RESET - val CommentColor = Console.BLUE - val KeywordColor = Console.YELLOW - val ValDefColor = Console.CYAN - val LiteralColor = Console.RED - val TypeColor = Console.MAGENTA - val AnnotationColor = Console.MAGENTA - - private def none(str: String) = str - private def keyword(str: String) = KeywordColor + str + NoColor - private def typeDef(str: String) = TypeColor + str + NoColor - private def literal(str: String) = LiteralColor + str + NoColor - private def valDef(str: String) = ValDefColor + str + NoColor - private def operator(str: String) = TypeColor + str + NoColor - private def annotation(str: String) = - if (str.trim == "@") str else AnnotationColor + str + NoColor - private val tripleQs = Console.RED_B + "???" + NoColor - - private val keywords: Seq[String] = for { - index <- IF to INLINE // All alpha keywords - } yield tokenString(index) - - private val interpolationPrefixes = - 'A' :: 'B' :: 'C' :: 'D' :: 'E' :: 'F' :: 'G' :: 'H' :: 'I' :: 'J' :: 'K' :: - 'L' :: 'M' :: 'N' :: 'O' :: 'P' :: 'Q' :: 'R' :: 'S' :: 'T' :: 'U' :: 'V' :: - 'W' :: 'X' :: 'Y' :: 'Z' :: '$' :: '_' :: 'a' :: 'b' :: 'c' :: 'd' :: 'e' :: - 'f' :: 'g' :: 'h' :: 'i' :: 'j' :: 'k' :: 'l' :: 'm' :: 'n' :: 'o' :: 'p' :: - 'q' :: 'r' :: 's' :: 't' :: 'u' :: 'v' :: 'w' :: 'x' :: 'y' :: 'z' :: Nil - - private val typeEnders = - '{' :: '}' :: ')' :: '(' :: '[' :: ']' :: '=' :: ' ' :: ',' :: '.' :: - '\n' :: Nil - - def apply(chars: Iterable[Char]): Iterable[Char] = { - var prev: Char = 0 - var remaining = chars.toStream - val newBuf = new StringBuilder - var lastToken = "" - - @inline def keywordStart = - prev == 0 || prev == ' ' || prev == '{' || prev == '(' || - prev == '\n' || prev == '[' || prev == ',' - - @inline def numberStart(c: Char) = - c.isDigit && (!prev.isLetter || prev == '.' || prev == ' ' || prev == '(' || prev == '\u0000') - - def takeChar(): Char = takeChars(1).head - def takeChars(x: Int): Seq[Char] = { - val taken = remaining.take(x) - remaining = remaining.drop(x) - taken - } - - while (remaining.nonEmpty) { - val n = takeChar() - if (interpolationPrefixes.contains(n)) { - // Interpolation prefixes are a superset of the keyword start chars - val next = remaining.take(3).mkString - if (next.startsWith("\"")) { - newBuf += n - prev = n - if (remaining.nonEmpty) takeChar() // drop 1 for appendLiteral - appendLiteral('"', next == "\"\"\"") - } else { - if (n.isUpper && keywordStart) { - appendWhile(n, !typeEnders.contains(_), typeDef) - } else if (keywordStart) { - append(n, keywords.contains(_), { kw => - if (kw == "new") typeDef(kw) else keyword(kw) - }) - } else { - newBuf += n - prev = n - } - } - } else { - (n: @switch) match { - case '/' => - if (remaining.nonEmpty) { - remaining.head match { - case '/' => - takeChar() - eolComment() - case '*' => - takeChar() - blockComment() - case x => - newBuf += '/' - } - } else newBuf += '/' - case '=' => - append('=', _ == "=>", operator) - case '<' => - append('<', { x => x == "<-" || x == "<:" || x == "<%" }, operator) - case '>' => - append('>', { x => x == ">:" }, operator) - case '#' => - if (prev != ' ' && prev != '.') newBuf append operator("#") - else newBuf += n - prev = '#' - case '@' => - appendWhile('@', !typeEnders.contains(_), annotation) - case '\"' => - appendLiteral('\"', multiline = remaining.take(2).mkString == "\"\"") - case '\'' => - appendLiteral('\'') - case '`' => - appendTo('`', _ == '`', none) - case _ => { - if (n == '?' && remaining.take(2).mkString == "??") { - takeChars(2) - newBuf append tripleQs - prev = '?' - } else if (n.isUpper && keywordStart) - appendWhile(n, !typeEnders.contains(_), typeDef) - else if (numberStart(n)) - appendWhile(n, { x => x.isDigit || x == '.' || x == '\u0000'}, literal) - else - newBuf += n; prev = n - } - } - } - } - - def eolComment() = { - newBuf append (CommentColor + "//") - var curr = '/' - while (curr != '\n' && remaining.nonEmpty) { - curr = takeChar() - newBuf += curr - } - prev = curr - newBuf append NoColor - } - - def blockComment() = { - newBuf append (CommentColor + "/*") - var curr = '*' - var open = 1 - while (open > 0 && remaining.nonEmpty) { - curr = takeChar() - newBuf += curr - - if (curr == '*' && remaining.nonEmpty) { - curr = takeChar() - newBuf += curr - if (curr == '/') open -= 1 - } else if (curr == '/' && remaining.nonEmpty) { - curr = takeChar() - newBuf += curr - if (curr == '*') open += 1 - } - } - prev = curr - newBuf append NoColor - } - - def appendLiteral(delim: Char, multiline: Boolean = false) = { - var curr: Char = 0 - var continue = true - var closing = 0 - val inInterpolation = interpolationPrefixes.contains(prev) - newBuf append (LiteralColor + delim) - - def shouldInterpolate = - inInterpolation && curr == '$' && prev != '$' && remaining.nonEmpty - - def interpolate() = { - val next = takeChar() - if (next == '$') { - newBuf += curr - newBuf += next - prev = '$' - } else if (next == '{') { - var open = 1 // keep track of open blocks - newBuf append (ValDefColor + curr) - newBuf += next - while (remaining.nonEmpty && open > 0) { - var c = takeChar() - newBuf += c - if (c == '}') open -= 1 - else if (c == '{') open += 1 - } - newBuf append LiteralColor - } else { - newBuf append (ValDefColor + curr) - newBuf += next - var c: Char = 'a' - while (c.isLetterOrDigit && remaining.nonEmpty) { - c = takeChar() - if (c != '"') newBuf += c - } - newBuf append LiteralColor - if (c == '"') { - newBuf += c - continue = false - } - } - closing = 0 - } - - while (continue && remaining.nonEmpty) { - curr = takeChar() - if (curr == '\\' && remaining.nonEmpty) { - val next = takeChar() - newBuf append (KeywordColor + curr) - if (next == 'u') { - val code = "u" + takeChars(4).mkString - newBuf append code - } else newBuf += next - newBuf append LiteralColor - closing = 0 - } else if (shouldInterpolate) { - interpolate() - } else if (curr == delim && multiline) { - closing += 1 - if (closing == 3) continue = false - newBuf += curr - } else if (curr == delim) { - continue = false - newBuf += curr - } else { - newBuf += curr - closing = 0 - } - } - newBuf append NoColor - prev = curr - } - - def append(c: Char, shouldHL: String => Boolean, highlight: String => String) = { - var curr: Char = 0 - val sb = new StringBuilder(s"$c") - - def delim(c: Char) = (c: @switch) match { - case ' ' => true - case '\n' => true - case '(' => true - case '[' => true - case ':' => true - case '@' => true - case _ => false - } - - while (remaining.nonEmpty && !delim(curr)) { - curr = takeChar() - if (!delim(curr)) sb += curr - } - - val str = sb.toString - val toAdd = - if (shouldHL(str)) - highlight(str) - else if (("var" :: "val" :: "def" :: "case" :: Nil).contains(lastToken)) - valDef(str) - else str - val suffix = if (delim(curr)) s"$curr" else "" - newBuf append (toAdd + suffix) - lastToken = str - prev = curr - } - - def appendWhile(c: Char, pred: Char => Boolean, highlight: String => String) = { - var curr: Char = 0 - val sb = new StringBuilder(s"$c") - while (remaining.nonEmpty && pred(curr)) { - curr = takeChar() - if (pred(curr)) sb += curr - } - - val str = sb.toString - val suffix = if (!pred(curr)) s"$curr" else "" - newBuf append (highlight(str) + suffix) - prev = curr - } - - def appendTo(c: Char, pred: Char => Boolean, highlight: String => String) = { - var curr: Char = 0 - val sb = new StringBuilder(s"$c") - while (remaining.nonEmpty && !pred(curr)) { - curr = takeChar() - sb += curr - } - - newBuf append highlight(sb.toString) - prev = curr - } - - newBuf.toIterable - } -} diff --git a/src/dotty/tools/dotc/printing/Texts.scala b/src/dotty/tools/dotc/printing/Texts.scala deleted file mode 100644 index db81cab7a..000000000 --- a/src/dotty/tools/dotc/printing/Texts.scala +++ /dev/null @@ -1,168 +0,0 @@ -package dotty.tools.dotc -package printing -import core.Contexts.Context -import language.implicitConversions - -object Texts { - - abstract class Text { - - protected def indentMargin = 2 - - def relems: List[Text] - - def isEmpty: Boolean = this match { - case Str(s) => s.isEmpty - case Fluid(relems) => relems forall (_.isEmpty) - case Vertical(relems) => relems.isEmpty - } - - def isVertical = isInstanceOf[Vertical] - def isClosed = isVertical || isInstanceOf[Closed] - def isFluid = isInstanceOf[Fluid] - def isSplittable = isFluid && !isClosed - - def close = new Closed(relems) - - def remaining(width: Int): Int = this match { - case Str(s) => - width - s.length - case Fluid(Nil) => - width - case Fluid(last :: prevs) => - val r = last remaining width - if (r < 0) r else Fluid(prevs) remaining r - case Vertical(_) => - -1 - } - - def lastLine: String = this match { - case Str(s) => s - case _ => relems.head.lastLine - } - - def appendToLastLine(that: Text): Text = that match { - case Str(s2) => - this match { - case Str(s1) => Str(s1 + s2) - case Fluid(Str(s1) :: prev) => Fluid(Str(s1 + s2) :: prev) - case Fluid(relems) => Fluid(that :: relems) - } - case Fluid(relems) => - (this /: relems.reverse)(_ appendToLastLine _) - } - - private def appendIndented(that: Text)(width: Int): Text = - Vertical(that.layout(width - indentMargin).indented :: this.relems) - - private def append(width: Int)(that: Text): Text = { - if (this.isEmpty) that.layout(width) - else if (that.isEmpty) this - else if (that.isVertical) appendIndented(that)(width) - else if (this.isVertical) Fluid(that.layout(width) :: this.relems) - else if (that.remaining(width - lastLine.length) >= 0) appendToLastLine(that) - else if (that.isSplittable) (this /: that.relems.reverse)(_.append(width)(_)) - else appendIndented(that)(width) - } - - def layout(width: Int): Text = this match { - case Str(_) => - this - case Fluid(relems) => - ((Str(""): Text) /: relems.reverse)(_.append(width)(_)) - case Vertical(relems) => - Vertical(relems map (_ layout width)) - } - - def map(f: String => String): Text = this match { - case Str(s) => Str(f(s)) - case Fluid(relems) => Fluid(relems map (_ map f)) - case Vertical(relems) => Vertical(relems map (_ map f)) - } - - def stripPrefix(pre: String): Text = this match { - case Str(s) => - if (s.startsWith(pre)) s drop pre.length else s - case Fluid(relems) => - val elems = relems.reverse - val head = elems.head.stripPrefix(pre) - if (head eq elems.head) this else Fluid((head :: elems.tail).reverse) - case Vertical(relems) => - val elems = relems.reverse - val head = elems.head.stripPrefix(pre) - if (head eq elems.head) this else Vertical((head :: elems.tail).reverse) - } - - private def indented: Text = this match { - case Str(s) => Str((" " * indentMargin) + s) - case Fluid(relems) => Fluid(relems map (_.indented)) - case Vertical(relems) => Vertical(relems map (_.indented)) - } - - def print(sb: StringBuilder): Unit = this match { - case Str(s) => - sb.append(s) - case _ => - var follow = false - for (elem <- relems.reverse) { - if (follow) sb.append("\n") - elem.print(sb) - follow = true - } - } - - def mkString(width: Int): String = { - val sb = new StringBuilder - layout(width).print(sb) - sb.toString - } - - def ~ (that: Text) = - if (this.isEmpty) that - else if (that.isEmpty) this - else Fluid(that :: this :: Nil) - - def ~~ (that: Text) = - if (this.isEmpty) that - else if (that.isEmpty) this - else Fluid(that :: Str(" ") :: this :: Nil) - - def over (that: Text) = - if (this.isVertical) Vertical(that :: this.relems) - else Vertical(that :: this :: Nil) - - def provided(pred: Boolean) = if (pred) this else Str("") - } - - object Text { - - /** The empty text */ - def apply(): Text = Str("") - - /** A concatenation of elements in `xs` and interspersed with - * separator strings `sep`. - */ - def apply(xs: Traversable[Text], sep: String = " "): Text = { - if (sep == "\n") lines(xs) - else { - val ys = xs filterNot (_.isEmpty) - if (ys.isEmpty) Str("") - else ys reduce (_ ~ sep ~ _) - } - } - - /** The given texts `xs`, each on a separate line */ - def lines(xs: Traversable[Text]) = Vertical(xs.toList.reverse) - } - - case class Str(s: String) extends Text { - override def relems: List[Text] = List(this) - } - - case class Vertical(relems: List[Text]) extends Text - case class Fluid(relems: List[Text]) extends Text - - class Closed(relems: List[Text]) extends Fluid(relems) - - implicit def stringToText(s: String): Text = Str(s) -} diff --git a/src/dotty/tools/dotc/printing/package.scala b/src/dotty/tools/dotc/printing/package.scala deleted file mode 100644 index 814eb2ad0..000000000 --- a/src/dotty/tools/dotc/printing/package.scala +++ /dev/null @@ -1,17 +0,0 @@ -package dotty.tools.dotc - -import core.StdNames.nme -import parsing.{precedence, minPrec, maxPrec, minInfixPrec} - -package object printing { - - type Precedence = Int - - val DotPrec = parsing.maxPrec - val AndPrec = parsing.precedence(nme.raw.AMP) - val OrPrec = parsing.precedence(nme.raw.BAR) - val InfixPrec = parsing.minInfixPrec - val GlobalPrec = parsing.minPrec - val TopLevelPrec = parsing.minPrec - 1 - -} diff --git a/src/dotty/tools/dotc/repl/AbstractFileClassLoader.scala b/src/dotty/tools/dotc/repl/AbstractFileClassLoader.scala deleted file mode 100644 index a3a463717..000000000 --- a/src/dotty/tools/dotc/repl/AbstractFileClassLoader.scala +++ /dev/null @@ -1,31 +0,0 @@ -package dotty.tools -package dotc -package repl - -import io.AbstractFile - -/** - * A class loader that loads files from a {@link scala.tools.nsc.io.AbstractFile}. - * - * @author Lex Spoon - */ -class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) -extends ClassLoader(parent) -{ - override def findClass(name: String): Class[_] = { - var file: AbstractFile = root - val pathParts = name.split("[./]").toList - for (dirPart <- pathParts.init) { - file = file.lookupName(dirPart, true) - if (file == null) { - throw new ClassNotFoundException(name) - } - } - file = file.lookupName(pathParts.last+".class", false) - if (file == null) { - throw new ClassNotFoundException(name) - } - val bytes = file.toByteArray - defineClass(name, bytes, 0, bytes.length) - } -} diff --git a/src/dotty/tools/dotc/repl/AmmoniteReader.scala b/src/dotty/tools/dotc/repl/AmmoniteReader.scala deleted file mode 100644 index f3b68e4b0..000000000 --- a/src/dotty/tools/dotc/repl/AmmoniteReader.scala +++ /dev/null @@ -1,82 +0,0 @@ -package dotty.tools -package dotc -package repl - -import core.Contexts._ -import ammonite.terminal._ -import LazyList._ -import Ansi.Color -import filters._ -import BasicFilters._ -import GUILikeFilters._ -import util.SourceFile -import printing.SyntaxHighlighting - -class AmmoniteReader(val interpreter: Interpreter)(implicit ctx: Context) extends InteractiveReader { - val interactive = true - - def incompleteInput(str: String): Boolean = - interpreter.delayOutputDuring(interpreter.interpret(str)) match { - case Interpreter.Incomplete => true - case _ => false - } - - val reader = new java.io.InputStreamReader(System.in) - val writer = new java.io.OutputStreamWriter(System.out) - val cutPasteFilter = ReadlineFilters.CutPasteFilter() - var history = List.empty[String] - val selectionFilter = GUILikeFilters.SelectionFilter(indent = 2) - val multilineFilter: Filter = Filter("multilineFilter") { - case TermState(lb ~: rest, b, c, _) - if (lb == 10 || lb == 13) && incompleteInput(b.mkString) => - BasicFilters.injectNewLine(b, c, rest, indent = 2) - } - - def readLine(prompt: String): String = { - val historyFilter = new HistoryFilter( - () => history.toVector, - Console.BLUE, - AnsiNav.resetForegroundColor - ) - - val allFilters = Filter.merge( - UndoFilter(), - historyFilter, - selectionFilter, - GUILikeFilters.altFilter, - GUILikeFilters.fnFilter, - ReadlineFilters.navFilter, - cutPasteFilter, - multilineFilter, - BasicFilters.all - ) - - Terminal.readLine( - Console.BLUE + prompt + Console.RESET, - reader, - writer, - allFilters, - displayTransform = (buffer, cursor) => { - val coloredBuffer = - if (ctx.useColors) SyntaxHighlighting(buffer) - else buffer - - val ansiBuffer = Ansi.Str.parse(coloredBuffer.toVector) - val (newBuffer, cursorOffset) = SelectionFilter.mangleBuffer( - selectionFilter, ansiBuffer, cursor, Ansi.Reversed.On - ) - val newNewBuffer = HistoryFilter.mangleBuffer( - historyFilter, newBuffer, cursor, - Ansi.Color.Green - ) - - (newNewBuffer, cursorOffset) - } - ) match { - case Some(res) => - history = res :: history; - res - case None => ":q" - } - } -} diff --git a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala deleted file mode 100644 index 5b3669d5e..000000000 --- a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala +++ /dev/null @@ -1,966 +0,0 @@ -package dotty.tools -package dotc -package repl - -import java.io.{ - File, PrintWriter, PrintStream, StringWriter, Writer, OutputStream, - ByteArrayOutputStream => ByteOutputStream -} -import java.lang.{Class, ClassLoader} -import java.net.{URL, URLClassLoader} - -import scala.collection.immutable.ListSet -import scala.collection.mutable -import scala.collection.mutable.{ListBuffer, HashSet, ArrayBuffer} - -//import ast.parser.SyntaxAnalyzer -import io.{PlainFile, VirtualDirectory} -import scala.reflect.io.{PlainDirectory, Directory} -import reporting.{ConsoleReporter, Reporter} -import core.Flags -import util.{SourceFile, NameTransformer} -import io.ClassPath -import ast.Trees._ -import parsing.Parsers._ -import core._ -import dotty.tools.backend.jvm.GenBCode -import Symbols._, Types._, Contexts._, StdNames._, Names._, NameOps._ -import Decorators._ -import scala.util.control.NonFatal -import printing.SyntaxHighlighting - -/** An interpreter for Scala code which is based on the `dotc` compiler. - * - * The overall approach is based on compiling the requested code and then - * using a Java classloader and Java reflection to run the code - * and access its results. - * - * In more detail, a single compiler instance is used - * to accumulate all successfully compiled or interpreted Scala code. To - * "interpret" a line of code, the compiler generates a fresh object that - * includes the line of code and which has public definition(s) to export - * all variables defined by that code. To extract the result of an - * interpreted line to show the user, a second "result object" is created - * which imports the variables exported by the above object and then - * exports a single definition named "result". To accommodate user expressions - * that read from variables or methods defined in previous statements, "import" - * statements are used. - * - * This interpreter shares the strengths and weaknesses of using the - * full compiler-to-Java. The main strength is that interpreted code - * behaves exactly as does compiled code, including running at full speed. - * The main weakness is that redefining classes and methods is not handled - * properly, because rebinding at the Java level is technically difficult. - * - * @author Moez A. Abdel-Gawad - * @author Lex Spoon - * @author Martin Odersky - * - * @param out The output to use for diagnostics - * @param ictx The context to use for initialization of the interpreter, - * needed to access the current classpath. - */ -class CompilingInterpreter( - out: PrintWriter, - ictx: Context, - parentClassLoader: Option[ClassLoader] -) extends Compiler with Interpreter { - import ast.untpd._ - import CompilingInterpreter._ - - ictx.base.initialize()(ictx) - - /** directory to save .class files to */ - val virtualDirectory = - if (ictx.settings.d.isDefault(ictx)) new VirtualDirectory("(memory)", None) - else new PlainDirectory(new Directory(new java.io.File(ictx.settings.d.value(ictx)))) // for now, to help debugging - - /** A GenBCode phase that uses `virtualDirectory` for its output */ - private class REPLGenBCode extends GenBCode { - override def outputDir(implicit ctx: Context) = virtualDirectory - } - - /** Phases of this compiler use `REPLGenBCode` instead of `GenBCode`. */ - override def phases = Phases.replace( - classOf[GenBCode], _ => new REPLGenBCode :: Nil, super.phases) - - /** whether to print out result lines */ - private var printResults: Boolean = true - private var delayOutput: Boolean = false - - val previousOutput = ListBuffer.empty[String] - - override def lastOutput() = { - val prev = previousOutput.toList - previousOutput.clear() - prev - } - - override def delayOutputDuring[T](operation: => T): T = { - val old = delayOutput - try { - delayOutput = true - operation - } finally { - delayOutput = old - } - } - - /** Temporarily be quiet */ - override def beQuietDuring[T](operation: => T): T = { - val wasPrinting = printResults - try { - printResults = false - operation - } finally { - printResults = wasPrinting - } - } - - private def newReporter = - new ConsoleReporter(Console.in, out) { - override def printMessage(msg: String) = - if (!delayOutput) { - out.print(/*clean*/(msg) + "\n") - // Suppress clean for now for compiler messages - // Otherwise we will completely delete all references to - // line$object$ module classes. The previous interpreter did not - // have the project because the module class was written without the final `$' - // and therefore escaped the purge. We can turn this back on once - // we drop the final `$' from module classes. - out.flush() - } else { - previousOutput += (/*clean*/(msg) + "\n") - } - } - - /** the previous requests this interpreter has processed */ - private val prevRequests = new ArrayBuffer[Request]() - - /** the compiler's classpath, as URL's */ - val compilerClasspath: List[URL] = ictx.platform.classPath(ictx).asURLs - - /* A single class loader is used for all commands interpreted by this Interpreter. - It would also be possible to create a new class loader for each command - to interpret. The advantages of the current approach are: - - - Expressions are only evaluated one time. This is especially - significant for I/O, e.g. "val x = Console.readLine" - - The main disadvantage is: - - - Objects, classes, and methods cannot be rebound. Instead, definitions - shadow the old ones, and old code objects refer to the old - definitions. - */ - /** class loader used to load compiled code */ - val classLoader: ClassLoader = { - lazy val parent = new URLClassLoader(compilerClasspath.toArray, - classOf[Interpreter].getClassLoader) - - new AbstractFileClassLoader(virtualDirectory, parentClassLoader.getOrElse(parent)) - } - - // Set the current Java "context" class loader to this interpreter's class loader - Thread.currentThread.setContextClassLoader(classLoader) - - /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */ - private def parse(line: String)(implicit ctx: Context): Option[List[Tree]] = { - var justNeedsMore = false - val reporter = newReporter - reporter.withIncompleteHandler { _ => _ => justNeedsMore = true } { - // simple parse: just parse it, nothing else - def simpleParse(code: String)(implicit ctx: Context): List[Tree] = { - val source = new SourceFile("<console>", code.toCharArray()) - val parser = new Parser(source) - val (selfDef, stats) = parser.templateStatSeq - stats - } - val trees = simpleParse(line)(ctx.fresh.setReporter(reporter)) - if (reporter.hasErrors) { - Some(Nil) // the result did not parse, so stop - } else if (justNeedsMore) { - None - } else { - Some(trees) - } - } - } - - /** Compile a SourceFile. Returns the root context of the run that compiled the file. - */ - def compileSources(sources: List[SourceFile])(implicit ctx: Context): Context = { - val reporter = newReporter - val run = newRun(ctx.fresh.setReporter(reporter)) - run.compileSources(sources) - run.runContext - } - - /** Compile a string. Returns true if there are no - * compilation errors, or false otherwise. - */ - def compileString(code: String)(implicit ctx: Context): Boolean = { - val runCtx = compileSources(List(new SourceFile("<script>", code.toCharArray))) - !runCtx.reporter.hasErrors - } - - override def interpret(line: String)(implicit ctx: Context): Interpreter.Result = { - // if (prevRequests.isEmpty) - // new Run(this) // initialize the compiler // (not sure this is needed) - // parse - parse(line) match { - case None => Interpreter.Incomplete - case Some(Nil) => Interpreter.Error // parse error or empty input - case Some(tree :: Nil) if tree.isTerm && !tree.isInstanceOf[Assign] => - previousOutput.clear() // clear previous error reporting - interpret(s"val $newVarName =\n$line") - case Some(trees) => - previousOutput.clear() // clear previous error reporting - val req = new Request(line, newLineName) - if (!req.compile()) - Interpreter.Error // an error happened during compilation, e.g. a type error - else { - val (resultStrings, succeeded) = req.loadAndRun() - if (delayOutput) - previousOutput ++= resultStrings.map(clean) - else if (printResults || !succeeded) - resultStrings.foreach(x => out.print(clean(x))) - if (succeeded) { - prevRequests += req - Interpreter.Success - } - else Interpreter.Error - } - } - } - - private def loadAndSetValue(objectName: String, value: AnyRef) = { - /** This terrible string is the wrapped class's full name inside the - * classloader: - * lineX$object$$iw$$iw$list$object - */ - val objName: String = List( - currentLineName + INTERPRETER_WRAPPER_SUFFIX, - INTERPRETER_IMPORT_WRAPPER, - INTERPRETER_IMPORT_WRAPPER, - objectName - ).mkString("$") - - try { - val resObj: Class[_] = Class.forName(objName, true, classLoader) - val setMethod = resObj.getDeclaredMethods.find(_.getName == "set") - - setMethod.fold(false) { method => - method.invoke(resObj, value) == null - } - } catch { - case NonFatal(_) => - // Unable to set value on object due to exception during reflection - false - } - } - - /** This bind is implemented by creating an object with a set method and a - * field `value`. The value is then set via Java reflection. - * - * Example: We want to bind a value `List(1,2,3)` to identifier `list` from - * sbt. The bind method accomplishes this by creating the following: - * {{{ - * object ContainerObjectWithUniqueID { - * var value: List[Int] = _ - * def set(x: Any) = value = x.asInstanceOf[List[Int]] - * } - * val list = ContainerObjectWithUniqueID.value - * }}} - * - * Between the object being created and the value being assigned, the value - * inside the object is set via reflection. - */ - override def bind(id: String, boundType: String, value: AnyRef)(implicit ctx: Context): Interpreter.Result = - interpret( - """ - |object %s { - | var value: %s = _ - | def set(x: Any) = value = x.asInstanceOf[%s] - |} - """.stripMargin.format(id + INTERPRETER_WRAPPER_SUFFIX, boundType, boundType) - ) match { - case Interpreter.Success if loadAndSetValue(id + INTERPRETER_WRAPPER_SUFFIX, value) => - val line = "val %s = %s.value".format(id, id + INTERPRETER_WRAPPER_SUFFIX) - interpret(line) - case Interpreter.Error | Interpreter.Incomplete => - out.println("Set failed in bind(%s, %s, %s)".format(id, boundType, value)) - Interpreter.Error - } - - /** Trait collecting info about one of the statements of an interpreter request */ - private trait StatementInfo { - /** The statement */ - def statement: Tree - - /** The names defined previously and referred to in the statement */ - def usedNames: List[Name] - - /** The names defined in the statement */ - val boundNames: List[Name] - - /** Statement is an import that contains a wildcard */ - val importsWildcard: Boolean - - /** The names imported by the statement (if it is an import clause) */ - val importedNames: Seq[Name] - - /** Statement defines an implicit calue or method */ - val definesImplicit: Boolean - } - - /** One line of code submitted by the user for interpretation */ - private class Request(val line: String, val lineName: String)(implicit ctx: Context) { - private val trees = { - val parsed = parse(line) - previousOutput.clear() // clear previous error reporting - parsed match { - case Some(ts) => ts - case None => Nil - } - } - - /** name to use for the object that will compute "line" */ - private def objectName = lineName + INTERPRETER_WRAPPER_SUFFIX - - /** name of the object that retrieves the result from the above object */ - private def resultObjectName = "RequestResult$" + objectName - - private def chooseHandler(stat: Tree): StatementHandler = stat match { - case stat: DefDef => new DefHandler(stat) - case stat: ValDef => new ValHandler(stat) - case stat: PatDef => new PatHandler(stat) - case stat @ Assign(Ident(_), _) => new AssignHandler(stat) - case stat: ModuleDef => new ModuleHandler(stat) - case stat: TypeDef if stat.isClassDef => new ClassHandler(stat) - case stat: TypeDef => new TypeAliasHandler(stat) - case stat: Import => new ImportHandler(stat) -// case DocDef(_, documented) => chooseHandler(documented) - case stat => new GenericHandler(stat) - } - - private val handlers: List[StatementHandler] = trees.map(chooseHandler) - - /** all (public) names defined by these statements */ - private val boundNames = ListSet(handlers.flatMap(_.boundNames): _*).toList - - /** list of names used by this expression */ - private val usedNames: List[Name] = handlers.flatMap(_.usedNames) - - private val (importsPreamble, importsTrailer, accessPath) = - importsCode(usedNames.toSet) - - /** Code to access a variable with the specified name */ - private def fullPath(vname: String): String = s"$objectName$accessPath.`$vname`" - - /** Code to access a variable with the specified name */ - private def fullPath(vname: Name): String = fullPath(vname.toString) - - /** the line of code to compute */ - private def toCompute = line - - /** generate the source code for the object that computes this request - * TODO Reformulate in a functional way - */ - private def objectSourceCode: String = - stringFrom { code => - // header for the wrapper object - code.println(s"object $objectName{") - code.print(importsPreamble) - code.println(toCompute) - handlers.foreach(_.extraCodeToEvaluate(this,code)) - code.println(importsTrailer) - //end the wrapper object - code.println(";}") - } - - /** Types of variables defined by this request. They are computed - after compilation of the main object */ - private var typeOf: Map[Name, String] = _ - - /** generate source code for the object that retrieves the result - from objectSourceCode */ - private def resultObjectSourceCode: String = - stringFrom(code => { - code.println(s"object $resultObjectName") - code.println("{ val result: String = {") - code.println(s"$objectName$accessPath;") // evaluate the object, to make sure its constructor is run - code.print("(\"\"") // print an initial empty string, so later code can - // uniformly be: + morestuff - handlers.foreach(_.resultExtractionCode(this, code)) - code.println("\n)}") - code.println(";}") - }) - - - /** Compile the object file. Returns whether the compilation succeeded. - * If all goes well, the "types" map is computed. */ - def compile(): Boolean = { - val compileCtx = compileSources( - List(new SourceFile("<console>", objectSourceCode.toCharArray))) - !compileCtx.reporter.hasErrors && { - this.typeOf = findTypes(compileCtx) - val resultCtx = compileSources( - List(new SourceFile("<console>", resultObjectSourceCode.toCharArray))) - !resultCtx.reporter.hasErrors - } - } - - /** Dig the types of all bound variables out of the compiler run. - * TODO: Change the interface so that we typecheck, and then transform - * directly. Treating the compiler as less of a blackbox will require - * much less magic here. - */ - private def findTypes(implicit ctx: Context): Map[Name, String] = { - def valAndVarNames = handlers.flatMap(_.valAndVarNames) - def defNames = handlers.flatMap(_.defNames) - - def getTypes(names: List[Name], nameMap: Name => Name): Map[Name, String] = { - /** the outermost wrapper object */ - val outerResObjSym: Symbol = - defn.EmptyPackageClass.info.decl(objectName.toTermName).symbol - - /** the innermost object inside the wrapper, found by - * following accessPath into the outer one. */ - val resObjSym = - (accessPath.split("\\.")).foldLeft(outerResObjSym) { (sym,str) => - if (str.isEmpty) sym - else - ctx.atPhase(ctx.typerPhase.next) { implicit ctx => - sym.info.member(str.toTermName).symbol - } - } - - names.foldLeft(Map.empty[Name,String]) { (map, name) => - val rawType = - ctx.atPhase(ctx.typerPhase.next) { implicit ctx => - resObjSym.info.member(name).info - } - - // the types are all =>T; remove the => - val cleanedType = rawType.widenExpr - - map + (name -> - ctx.atPhase(ctx.typerPhase.next) { implicit ctx => - cleanedType.show - }) - } - } - - val names1 = getTypes(valAndVarNames, n => n.toTermName.fieldName) - val names2 = getTypes(defNames, identity) - names1 ++ names2 - } - - /** Sets both System.{out,err} and Console.{out,err} to supplied - * `os: OutputStream` - */ - private def withOutput[T](os: ByteOutputStream)(op: ByteOutputStream => T) = { - val ps = new PrintStream(os) - val oldOut = System.out - val oldErr = System.err - System.setOut(ps) - System.setErr(ps) - - try { - Console.withOut(os)(Console.withErr(os)(op(os))) - } finally { - System.setOut(oldOut) - System.setErr(oldErr) - } - } - - /** load and run the code using reflection. - * @return A pair consisting of the run's result as a `List[String]`, and - * a boolean indicating whether the run succeeded without throwing - * an exception. - */ - def loadAndRun(): (List[String], Boolean) = { - val interpreterResultObject: Class[_] = - Class.forName(resultObjectName, true, classLoader) - val valMethodRes: java.lang.reflect.Method = - interpreterResultObject.getMethod("result") - try { - withOutput(new ByteOutputStream) { ps => - val rawRes = valMethodRes.invoke(interpreterResultObject).toString - val res = - if (ictx.useColors) new String(SyntaxHighlighting(rawRes).toArray) - else rawRes - val prints = ps.toString("utf-8") - val printList = if (prints != "") prints :: Nil else Nil - - if (!delayOutput) out.print(prints) - - (printList :+ res, true) - } - } catch { - case NonFatal(ex) => - def cause(ex: Throwable): Throwable = - if (ex.getCause eq null) ex else cause(ex.getCause) - val orig = cause(ex) - (stringFrom(str => orig.printStackTrace(str)) :: Nil, false) - } - } - - /** Compute imports that allow definitions from previous - * requests to be visible in a new request. Returns - * three pieces of related code as strings: - * - * 1. A _preamble_: An initial code fragment that should go before - * the code of the new request. - * - * 2. A _trailer_: A code fragment that should go after the code - * of the new request. - * - * 3. An _access path_ which can be traversed to access - * any bindings inside code wrapped by #1 and #2 . - * - * The argument is a set of Names that need to be imported. - * - * Limitations: This method is not as precise as it could be. - * (1) It does not process wildcard imports to see what exactly - * they import. - * (2) If it imports any names from a request, it imports all - * of them, which is not really necessary. - * (3) It imports multiple same-named implicits, but only the - * last one imported is actually usable. - */ - private def importsCode(wanted: Set[Name]): (String, String, String) = { - /** Narrow down the list of requests from which imports - * should be taken. Removes requests which cannot contribute - * useful imports for the specified set of wanted names. - */ - def reqsToUse: List[(Request, StatementInfo)] = { - /** Loop through a list of StatementHandlers and select - * which ones to keep. 'wanted' is the set of - * names that need to be imported. - */ - def select(reqs: List[(Request, StatementInfo)], wanted: Set[Name]): List[(Request, StatementInfo)] = { - reqs match { - case Nil => Nil - - case (req, handler) :: rest => - val keepit = - (handler.definesImplicit || - handler.importsWildcard || - handler.importedNames.exists(wanted.contains(_)) || - handler.boundNames.exists(wanted.contains(_))) - - val newWanted = - if (keepit) { - (wanted - ++ handler.usedNames - -- handler.boundNames - -- handler.importedNames) - } else { - wanted - } - - val restToKeep = select(rest, newWanted) - - if (keepit) - (req, handler) :: restToKeep - else - restToKeep - } - } - - val rhpairs = for { - req <- prevRequests.toList.reverse - handler <- req.handlers - } yield (req, handler) - - select(rhpairs, wanted).reverse - } - - val preamble = new StringBuffer - val trailingBraces = new StringBuffer - val accessPath = new StringBuffer - val impname = INTERPRETER_IMPORT_WRAPPER - val currentImps = mutable.Set[Name]() - - // add code for a new object to hold some imports - def addWrapper(): Unit = { - preamble.append("object " + impname + "{\n") - trailingBraces.append("}\n") - accessPath.append("." + impname) - currentImps.clear - } - - addWrapper() - - // loop through previous requests, adding imports - // for each one - for ((req, handler) <- reqsToUse) { - // If the user entered an import, then just use it - - // add an import wrapping level if the import might - // conflict with some other import - if (handler.importsWildcard || - currentImps.exists(handler.importedNames.contains)) - if (!currentImps.isEmpty) - addWrapper() - - if (handler.statement.isInstanceOf[Import]) - preamble.append(handler.statement.show + ";\n") - - // give wildcard imports a import wrapper all to their own - if (handler.importsWildcard) - addWrapper() - else - currentImps ++= handler.importedNames - - // For other requests, import each bound variable. - // import them explicitly instead of with _, so that - // ambiguity errors will not be generated. Also, quote - // the name of the variable, so that we don't need to - // handle quoting keywords separately. - for (imv <- handler.boundNames) { - if (currentImps.contains(imv)) - addWrapper() - preamble.append("import ") - preamble.append(req.objectName + req.accessPath + ".`" + imv + "`;\n") - currentImps += imv - } - } - - addWrapper() // Add one extra wrapper, to prevent warnings - // in the frequent case of redefining - // the value bound in the last interpreter - // request. - - (preamble.toString, trailingBraces.toString, accessPath.toString) - } - - // ------ Handlers ------------------------------------------ - - /** Class to handle one statement among all the statements included - * in a single interpreter request. - */ - private sealed abstract class StatementHandler(val statement: Tree) extends StatementInfo { - val usedNames: List[Name] = { - val ivt = new UntypedTreeAccumulator[mutable.Set[Name]] { - override def apply(ns: mutable.Set[Name], tree: Tree)(implicit ctx: Context) = - tree match { - case Ident(name) => ns += name - case _ => foldOver(ns, tree) - } - } - ivt.foldOver(HashSet(), statement).toList - } - val boundNames: List[Name] = Nil - def valAndVarNames: List[Name] = Nil - def defNames: List[Name] = Nil - val importsWildcard = false - val importedNames: Seq[Name] = Nil - val definesImplicit = statement match { - case tree: MemberDef => tree.mods.is(Flags.Implicit) - case _ => false - } - - def extraCodeToEvaluate(req: Request, code: PrintWriter) = {} - def resultExtractionCode(req: Request, code: PrintWriter) = {} - } - - private class GenericHandler(statement: Tree) extends StatementHandler(statement) - - private abstract class ValOrPatHandler(statement: Tree) - extends StatementHandler(statement) { - override val boundNames: List[Name] = _boundNames - override def valAndVarNames = boundNames - - override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - if (!shouldShowResult(req)) return - val resultExtractors = boundNames.map(name => resultExtractor(req, name)) - code.print(resultExtractors.mkString("")) - } - - private def resultExtractor(req: Request, varName: Name): String = { - val prettyName = varName.decode - val varType = string2code(req.typeOf(varName)) - val fullPath = req.fullPath(varName) - - s""" + "$prettyName: $varType = " + { - | if ($fullPath.asInstanceOf[AnyRef] != null) { - | (if ($fullPath.toString().contains('\\n')) "\\n" else "") + - | $fullPath.toString() + "\\n" - | } else { - | "null\\n" - | } - |}""".stripMargin - } - - protected def _boundNames: List[Name] - protected def shouldShowResult(req: Request): Boolean - } - - private class ValHandler(statement: ValDef) extends ValOrPatHandler(statement) { - override def _boundNames = List(statement.name) - - override def shouldShowResult(req: Request): Boolean = - !statement.mods.is(Flags.AccessFlags) && - !(isGeneratedVarName(statement.name.toString) && - req.typeOf(statement.name.encode) == "Unit") - } - - - private class PatHandler(statement: PatDef) extends ValOrPatHandler(statement) { - override def _boundNames = statement.pats.flatMap(findVariableNames) - - override def shouldShowResult(req: Request): Boolean = - !statement.mods.is(Flags.AccessFlags) - - private def findVariableNames(tree: Tree): List[Name] = tree match { - case Ident(name) if name.toString != "_" => List(name) - case _ => VariableNameFinder(Nil, tree).reverse - } - - private object VariableNameFinder extends UntypedDeepFolder[List[Name]]( - (acc: List[Name], t: Tree) => t match { - case _: BackquotedIdent => acc - case Ident(name) if name.isVariableName && name.toString != "_" => name :: acc - case Bind(name, _) if name.isVariableName => name :: acc - case _ => acc - } - ) - } - - private class DefHandler(defDef: DefDef) extends StatementHandler(defDef) { - override val boundNames = List(defDef.name) - override def defNames = boundNames - - override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - if (!defDef.mods.is(Flags.AccessFlags)) - code.print("+\"" + string2code(defDef.name.toString) + ": " + - string2code(req.typeOf(defDef.name)) + "\\n\"") - } - } - - private class AssignHandler(statement: Assign) extends StatementHandler(statement) { - val lhs = statement.lhs.asInstanceOf[Ident] // an unfortunate limitation - - val helperName = newInternalVarName().toTermName - override val valAndVarNames = List(helperName) - - override def extraCodeToEvaluate(req: Request, code: PrintWriter): Unit = { - code.println(i"val $helperName = ${statement.lhs};") - } - - /** Print out lhs instead of the generated varName */ - override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - code.print(" + \"" + lhs.show + ": " + - string2code(req.typeOf(helperName.encode)) + - " = \" + " + - string2code(req.fullPath(helperName)) - + " + \"\\n\"") - } - } - - private class ModuleHandler(module: ModuleDef) extends StatementHandler(module) { - override val boundNames = List(module.name) - - override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - code.println(" + \"defined module " + - string2code(module.name.toString) - + "\\n\"") - } - } - - private class ClassHandler(classdef: TypeDef) - extends StatementHandler(classdef) { - override val boundNames = - List(classdef.name) ::: - (if (classdef.mods.is(Flags.Case)) - List(classdef.name.toTermName) - else - Nil) - - // TODO: MemberDef.keyword does not include "trait"; - // otherwise it could be used here - def keyword: String = - if (classdef.mods.is(Flags.Trait)) "trait" else "class" - - override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - code.print( - " + \"defined " + - keyword + - " " + - string2code(classdef.name.toString) + - "\\n\"") - } - } - - private class TypeAliasHandler(typeDef: TypeDef) - extends StatementHandler(typeDef) { - override val boundNames = - if (!typeDef.mods.is(Flags.AccessFlags) && !typeDef.rhs.isInstanceOf[TypeBoundsTree]) - List(typeDef.name) - else - Nil - - override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - code.println(" + \"defined type alias " + - string2code(typeDef.name.toString) + "\\n\"") - } - } - - private class ImportHandler(imp: Import) extends StatementHandler(imp) { - override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - code.println("+ \"" + imp.show + "\\n\"") - } - - def isWildcardSelector(tree: Tree) = tree match { - case Ident(nme.USCOREkw) => true - case _ => false - } - - /** Whether this import includes a wildcard import */ - override val importsWildcard = imp.selectors.exists(isWildcardSelector) - - /** The individual names imported by this statement */ - override val importedNames: Seq[Name] = - imp.selectors.filterNot(isWildcardSelector).flatMap { - case sel: RefTree => List(sel.name.toTypeName, sel.name.toTermName) - case _ => Nil - } - } - - } // end Request - - // ------- String handling ---------------------------------- - - /** next line number to use */ - private var nextLineNo = 0 - - /** allocate a fresh line name */ - private def newLineName = { - val num = nextLineNo - nextLineNo += 1 - INTERPRETER_LINE_PREFIX + num - } - - private def currentLineName = - INTERPRETER_LINE_PREFIX + (nextLineNo - 1) - - /** next result variable number to use */ - private var nextVarNameNo = 0 - - /** allocate a fresh variable name */ - private def newVarName = { - val num = nextVarNameNo - nextVarNameNo += 1 - INTERPRETER_VAR_PREFIX + num - } - - /** next internal variable number to use */ - private var nextInternalVarNo = 0 - - /** allocate a fresh internal variable name */ - private def newInternalVarName() = { - val num = nextVarNameNo - nextVarNameNo += 1 - INTERPRETER_SYNTHVAR_PREFIX + num - } - - /** Check if a name looks like it was generated by newVarName */ - private def isGeneratedVarName(name: String): Boolean = - name.startsWith(INTERPRETER_VAR_PREFIX) && { - val suffix = name.drop(INTERPRETER_VAR_PREFIX.length) - suffix.forall(_.isDigit) - } - - /** generate a string using a routine that wants to write on a stream */ - private def stringFrom(writer: PrintWriter => Unit): String = { - val stringWriter = new StringWriter() - val stream = new NewLinePrintWriter(stringWriter) - writer(stream) - stream.close() - stringWriter.toString - } - - /** Truncate a string if it is longer than settings.maxPrintString */ - private def truncPrintString(str: String)(implicit ctx: Context): String = { - val maxpr = ctx.settings.XreplLineWidth.value - - if (maxpr <= 0) - return str - - if (str.length <= maxpr) - return str - - val trailer = "..." - if (maxpr >= trailer.length-1) - str.substring(0, maxpr-3) + trailer + "\n" - else - str.substring(0, maxpr-1) - } - - /** Clean up a string for output */ - private def clean(str: String)(implicit ctx: Context) = - truncPrintString(stripWrapperGunk(str)) -} - -/** Utility methods for the Interpreter. */ -object CompilingInterpreter { - val INTERPRETER_WRAPPER_SUFFIX = "$object" - val INTERPRETER_LINE_PREFIX = "line" - val INTERPRETER_VAR_PREFIX = "res" - val INTERPRETER_IMPORT_WRAPPER = "$iw" - val INTERPRETER_SYNTHVAR_PREFIX = "synthvar$" - - /** Delete a directory tree recursively. Use with care! - */ - private[repl] def deleteRecursively(path: File): Unit = { - path match { - case _ if !path.exists => - () - case _ if path.isDirectory => - for (p <- path.listFiles) - deleteRecursively(p) - path.delete - case _ => - path.delete - } - } - - /** Heuristically strip interpreter wrapper prefixes - * from an interpreter output string. - */ - def stripWrapperGunk(str: String): String = { - val wrapregex = "(line[0-9]+\\$object[$.])?(\\$iw[$.])*" - str.replaceAll(wrapregex, "") - } - - /** Convert a string into code that can recreate the string. - * This requires replacing all special characters by escape - * codes. It does not add the surrounding " marks. */ - def string2code(str: String): String = { - /** Convert a character to a backslash-u escape */ - def char2uescape(c: Char): String = { - var rest = c.toInt - val buf = new StringBuilder - for (i <- 1 to 4) { - buf ++= (rest % 16).toHexString - rest = rest / 16 - } - "\\" + "u" + buf.toString.reverse - } - val res = new StringBuilder - for (c <- str) { - if ("'\"\\" contains c) { - res += '\\' - res += c - } else if (!c.isControl) { - res += c - } else { - res ++= char2uescape(c) - } - } - res.toString - } -} diff --git a/src/dotty/tools/dotc/repl/ConsoleWriter.scala b/src/dotty/tools/dotc/repl/ConsoleWriter.scala deleted file mode 100644 index 9387f366a..000000000 --- a/src/dotty/tools/dotc/repl/ConsoleWriter.scala +++ /dev/null @@ -1,21 +0,0 @@ -package dotty.tools -package dotc -package repl -import java.io.Writer - -/** A Writer that writes onto the Scala Console. - * - * @author Lex Spoon - * @version 1.0 - */ -class ConsoleWriter extends Writer { - def close = flush - - def flush = Console.flush - - def write(cbuf: Array[Char], off: Int, len: Int): Unit = - if (len > 0) - write(new String(cbuf, off, len)) - - override def write(str: String): Unit = Console.print(str) -} diff --git a/src/dotty/tools/dotc/repl/InteractiveReader.scala b/src/dotty/tools/dotc/repl/InteractiveReader.scala deleted file mode 100644 index 07ce23717..000000000 --- a/src/dotty/tools/dotc/repl/InteractiveReader.scala +++ /dev/null @@ -1,20 +0,0 @@ -package dotty.tools -package dotc -package repl - -import dotc.core.Contexts.Context - -/** Reads lines from an input stream */ -trait InteractiveReader { - def readLine(prompt: String): String - val interactive: Boolean -} - -/** The current Scala REPL know how to do this flexibly. - */ -object InteractiveReader { - /** Create an interactive reader */ - def createDefault(in: Interpreter)(implicit ctx: Context): InteractiveReader = { - new AmmoniteReader(in) - } -} diff --git a/src/dotty/tools/dotc/repl/Interpreter.scala b/src/dotty/tools/dotc/repl/Interpreter.scala deleted file mode 100644 index edcc5b153..000000000 --- a/src/dotty/tools/dotc/repl/Interpreter.scala +++ /dev/null @@ -1,45 +0,0 @@ -package dotty.tools -package dotc -package repl - -import core.Contexts.Context - -/** This object defines the type of interpreter results */ -object Interpreter { - - /** A result from interpreting one line of input. */ - abstract sealed class Result - - /** The line was interpreted successfully. */ - case object Success extends Result - - /** The line was erroneous in some way. */ - case object Error extends Result - - /** The input was incomplete. The caller should request more input. - */ - case object Incomplete extends Result -} - -/** The exported functionality of the interpreter */ -trait Interpreter { - import Interpreter._ - - /** Interpret one line of input. All feedback, including parse errors and - * evaluation results, are printed via the context's reporter. Values - * defined are available for future interpreted strings. - */ - def interpret(line: String)(implicit ctx: Context): Result - - /** Tries to bind an id to a value, returns the outcome of trying to bind */ - def bind(id: String, boundType: String, value: AnyRef)(implicit ctx: Context): Result - - /** Suppress output during evaluation of `operation`. */ - def beQuietDuring[T](operation: => T): T - - /** Suppresses output and saves it for `lastOutput` to collect */ - def delayOutputDuring[T](operation: => T): T - - /** Gets the last output not printed immediately */ - def lastOutput(): Seq[String] -} diff --git a/src/dotty/tools/dotc/repl/InterpreterLoop.scala b/src/dotty/tools/dotc/repl/InterpreterLoop.scala deleted file mode 100644 index b3ac41c55..000000000 --- a/src/dotty/tools/dotc/repl/InterpreterLoop.scala +++ /dev/null @@ -1,210 +0,0 @@ -package dotty.tools -package dotc -package repl - -import java.io.{BufferedReader, File, FileReader, PrintWriter} -import java.io.IOException -import java.lang.{ClassLoader, System} -import scala.concurrent.{Future, Await} -import scala.concurrent.duration.Duration -import reporting.Reporter -import core._ -import Contexts._ -import annotation.tailrec -import scala.concurrent.ExecutionContext.Implicits.global - -/** The interactive shell. It provides a read-eval-print loop around - * the Interpreter class. - * After instantiation, clients should call the `run` method. - * - * @author Moez A. Abdel-Gawad - * @author Lex Spoon - * @author Martin Odersky - */ -class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Context) { - import config._ - - val interpreter = compiler.asInstanceOf[Interpreter] - - private var in = input(interpreter) - - /** The context class loader at the time this object was created */ - protected val originalClassLoader = - Thread.currentThread.getContextClassLoader - - /** A reverse list of commands to replay if the user - * requests a :replay */ - var replayCommandsRev: List[String] = Nil - - /** A list of commands to replay if the user requests a :replay */ - def replayCommands = replayCommandsRev.reverse - - /** Record a command for replay should the user request a :replay */ - def addReplay(cmd: String) = - replayCommandsRev = cmd :: replayCommandsRev - - /** Close the interpreter */ - def closeInterpreter()(implicit ctx: Context): Unit = { - ctx.reporter.flush() - Thread.currentThread.setContextClassLoader(originalClassLoader) - } - - /** print a friendly help message */ - def printHelp(): Unit = { - printWelcome() - output.println("Type :load followed by a filename to load a Scala file.") - output.println("Type :replay to reset execution and replay all previous commands.") - output.println("Type :quit to exit the interpreter.") - } - - /** Print a welcome message */ - def printWelcome(): Unit = { - output.println(s"Welcome to Scala$version " + " (" + - System.getProperty("java.vm.name") + ", Java " + System.getProperty("java.version") + ")." ) - output.println("Type in expressions to have them evaluated.") - output.println("Type :help for more information.") - output.flush() - } - - val gitHash = ManifestInfo.attributes.getOrElse("Git-Hash", "unknown") - val version = s".next (pre-alpha, git-hash: $gitHash)" - - /** The main read-eval-print loop for the interpreter. It calls - * `command()` for each line of input. - */ - @tailrec final def repl(line: String = in.readLine(prompt)): Unit = - if (line != null) { - val (keepGoing, finalLineOpt) = command(line) - if (keepGoing) { - finalLineOpt.foreach(addReplay) - output.flush() - repl() - } - } - - /** interpret all lines from a specified file */ - def interpretAllFrom(filename: String): Unit = { - import java.nio.file.{Files, Paths} - import scala.collection.JavaConversions._ - try { - val lines = Files.readAllLines(Paths.get(filename)).mkString("\n") - output.println("Loading " + filename + "...") - output.flush - interpreter.interpret(lines) - } catch { - case _: IOException => - output.println("Error opening file: " + filename) - } - } - - /** create a new interpreter and replay all commands so far */ - def replay(): Unit = { - for (cmd <- replayCommands) { - output.println("Replaying: " + cmd) - output.flush() // because maybe cmd will have its own output - command(cmd) - output.println - } - } - - /** Run one command submitted by the user. Three values are returned: - * (1) whether to keep running, (2) the line to record for replay, - * if any. */ - def command(line: String): (Boolean, Option[String]) = { - def withFile(command: String)(action: String => Unit): Unit = { - val spaceIdx = command.indexOf(' ') - if (spaceIdx <= 0) { - output.println("That command requires a filename to be specified.") - return - } - val filename = command.substring(spaceIdx).trim - if (!new File(filename).exists) { - output.println("That file does not exist") - return - } - action(filename) - } - - val helpRegexp = ":h(e(l(p)?)?)?" - val quitRegexp = ":q(u(i(t)?)?)?" - val loadRegexp = ":l(o(a(d)?)?)?.*" - val replayRegexp = ":r(e(p(l(a(y)?)?)?)?)?.*" - val lastOutput = interpreter.lastOutput() - - var shouldReplay: Option[String] = None - - if (line.matches(helpRegexp)) - printHelp() - else if (line.matches(quitRegexp)) - return (false, None) - else if (line.matches(loadRegexp)) { - withFile(line)(f => { - interpretAllFrom(f) - shouldReplay = Some(line) - }) - } - else if (line matches replayRegexp) - replay() - else if (line startsWith ":") - output.println("Unknown command. Type :help for help.") - else - shouldReplay = lastOutput match { // don't interpret twice - case Nil => interpretStartingWith(line) - case oldRes => - oldRes foreach output.print - Some(line) - } - - (true, shouldReplay) - } - - def silentlyRun(cmds: List[String]): Unit = cmds.foreach { cmd => - interpreter.beQuietDuring(interpreter.interpret(cmd)) - } - - def silentlyBind(values: Array[(String, Any)]): Unit = values.foreach { case (id, value) => - interpreter.beQuietDuring( - interpreter.bind(id, value.asInstanceOf[AnyRef].getClass.getName, value.asInstanceOf[AnyRef])) - } - - /** Interpret expressions starting with the first line. - * Read lines until a complete compilation unit is available - * or until a syntax error has been seen. If a full unit is - * read, go ahead and interpret it. Return the full string - * to be recorded for replay, if any. - */ - def interpretStartingWith(code: String): Option[String] = - interpreter.interpret(code) match { - case Interpreter.Success => Some(code) - case _ => None - } -/* - def loadFiles(settings: Settings) { - settings match { - case settings: GenericRunnerSettings => - for (filename <- settings.loadfiles.value) { - val cmd = ":load " + filename - command(cmd) - replayCommandsRev = cmd :: replayCommandsRev - output.println() - } - case _ => - } - } -*/ - def run(): Reporter = { - // loadFiles(settings) - try { - if (!ctx.reporter.hasErrors) { // if there are already errors, no sense to continue - printWelcome() - silentlyRun(config.initialCommands) - silentlyBind(config.boundValues) - repl(in.readLine(prompt)) - silentlyRun(config.cleanupCommands) - } - } finally { - closeInterpreter() - } - ctx.reporter - } -} diff --git a/src/dotty/tools/dotc/repl/Main.scala b/src/dotty/tools/dotc/repl/Main.scala deleted file mode 100644 index 48ed3e788..000000000 --- a/src/dotty/tools/dotc/repl/Main.scala +++ /dev/null @@ -1,28 +0,0 @@ -package dotty.tools -package dotc -package repl - -/* This REPL was adapted from an old (2008-ish) version of the Scala - * REPL. The original version from which the adaptation was done is found in: - * - * https://github.com/odersky/legacy-svn-scala/tree/spoon - * - * The reason this version was picked instead of a more current one is that - * the older version is much smaller, therefore easier to port. It is also - * considerably less intertwined with nsc than later versions. - * - * There are a number of TODOs: - * - * - figure out why we can launch REPL only with `java`, not with `scala`. - * - make a doti command (urgent, easy) - * - create or port REPL tests (urgent, intermediate) - * - copy improvements of current Scala REPL wrt to this version - * (somewhat urgent, intermediate) - * - re-enable bindSettings (not urgent, easy, see TODO in InterpreterLoop.scala) - * - make string generation more functional (not urgent, easy) - * - better handling of ^C (not urgent, intermediate) - * - syntax highlighting (not urgent, intermediate) - * - integrate with presentation compiler for command completion (not urgent, hard) - */ -/** The main entry point of the REPL */ -object Main extends REPL diff --git a/src/dotty/tools/dotc/repl/ManifestInfo.scala b/src/dotty/tools/dotc/repl/ManifestInfo.scala deleted file mode 100644 index 206dccd67..000000000 --- a/src/dotty/tools/dotc/repl/ManifestInfo.scala +++ /dev/null @@ -1,20 +0,0 @@ -package dotty.tools.dotc.repl - -import java.net.JarURLConnection -import scala.collection.JavaConversions._ - -object ManifestInfo { - - val attributes: Map[String, String] = { - for { - resourceUrl <- Option(getClass.getResource(getClass.getSimpleName + ".class")) - urlConnection = resourceUrl.openConnection() if urlConnection.isInstanceOf[JarURLConnection] - manifest <- Option(urlConnection.asInstanceOf[JarURLConnection].getManifest) - } yield { - manifest.getMainAttributes.foldLeft(Map[String, String]())( - (map, attribute) => map + (attribute._1.toString -> attribute._2.toString) - ) - } - }.getOrElse(Map()) - -} diff --git a/src/dotty/tools/dotc/repl/NewLinePrintWriter.scala b/src/dotty/tools/dotc/repl/NewLinePrintWriter.scala deleted file mode 100644 index 8e36a0ae4..000000000 --- a/src/dotty/tools/dotc/repl/NewLinePrintWriter.scala +++ /dev/null @@ -1,11 +0,0 @@ -package dotty.tools -package dotc -package repl -import java.io.{Writer, PrintWriter} - -class NewLinePrintWriter(out: Writer, autoFlush: Boolean) -extends PrintWriter(out, autoFlush) { - def this(out: Writer) = this(out, false) - override def println(): Unit = { print("\n"); flush() } -} - diff --git a/src/dotty/tools/dotc/repl/REPL.scala b/src/dotty/tools/dotc/repl/REPL.scala deleted file mode 100644 index 211e3c931..000000000 --- a/src/dotty/tools/dotc/repl/REPL.scala +++ /dev/null @@ -1,100 +0,0 @@ -package dotty.tools -package dotc -package repl - -import core.Contexts.Context -import reporting.Reporter -import io.{AbstractFile, PlainFile, VirtualDirectory} -import scala.reflect.io.{PlainDirectory, Directory} -import java.io.{BufferedReader, File => JFile, FileReader, PrintWriter} -import java.net.{URL, URLClassLoader} - -/** A compiler which stays resident between runs. - * Usage: - * - * > scala dotty.tools.dotc.Resident <options> <initial files> - * - * dotc> "more options and files to compile" - * - * ... - * - * dotc> :reset // reset all options to the ones passed on the command line - * - * ... - * - * dotc> :q // quit - */ -class REPL extends Driver { - - lazy val config = new REPL.Config - - override def setup(args: Array[String], rootCtx: Context): (List[String], Context) = { - val (strs, ctx) = super.setup(args, rootCtx) - (strs, config.context(ctx)) - } - - override def newCompiler(implicit ctx: Context): Compiler = - new repl.CompilingInterpreter(config.output, ctx, config.classLoader) - - override def sourcesRequired = false - - override def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = { - if (fileNames.isEmpty) - new InterpreterLoop(compiler, config).run() - else - ctx.error(s"don't now what to do with $fileNames%, %") - ctx.reporter - } -} - -object REPL { - class Config { - val prompt = "scala> " - val continuationPrompt = " " - val version = ".next (pre-alpha)" - - def context(ctx: Context): Context = ctx - - /** The first interpreted commands always take a couple of seconds due to - * classloading. To bridge the gap, we warm up the interpreter by letting - * it interpret at least a dummy line while waiting for the first line of - * input to be entered. - */ - val initialCommands: List[String] = - "val theAnswerToLifeInTheUniverseAndEverything = 21 * 2" :: Nil - - /** Before exiting, the interpreter will also run the cleanup commands - * issued in the variable below. This is useful if your REPL creates - * things during its run that should be dealt with before shutdown. - */ - val cleanupCommands: List[String] = Nil - - /** Initial values in the REPL can also be bound from runtime. Override - * this variable in the following manner to bind a variable at the start - * of the REPL session: - * - * {{{ - * override val boundValues = Array("exampleList" -> List(1, 1, 2, 3, 5)) - * }}} - * - * This is useful if you've integrated the REPL as part of your project - * and already have objects available during runtime that you'd like to - * inspect. - */ - val boundValues: Array[(String, Any)] = Array.empty[(String, Any)] - - /** To pass a custom ClassLoader to the Dotty REPL, overwride this value */ - val classLoader: Option[ClassLoader] = None - - /** The default input reader */ - def input(in: Interpreter)(implicit ctx: Context): InteractiveReader = { - val emacsShell = System.getProperty("env.emacs", "") != "" - //println("emacsShell="+emacsShell) //debug - if (emacsShell) new SimpleReader() - else InteractiveReader.createDefault(in) - } - - /** The default output writer */ - def output: PrintWriter = new NewLinePrintWriter(new ConsoleWriter, true) - } -} diff --git a/src/dotty/tools/dotc/repl/SimpleReader.scala b/src/dotty/tools/dotc/repl/SimpleReader.scala deleted file mode 100644 index 5fab47bbe..000000000 --- a/src/dotty/tools/dotc/repl/SimpleReader.scala +++ /dev/null @@ -1,24 +0,0 @@ -package dotty.tools -package dotc -package repl - -import java.io.{BufferedReader, PrintWriter} -import dotc.core.Contexts.Context - - -/** Reads using standard JDK API */ -class SimpleReader( - in: BufferedReader, - out: PrintWriter, - val interactive: Boolean) -extends InteractiveReader { - def this() = this(Console.in, new PrintWriter(Console.out), true) - - def readLine(prompt: String) = { - if (interactive) { - out.print(prompt) - out.flush() - } - in.readLine() - } -} diff --git a/src/dotty/tools/dotc/repl/ammonite/Ansi.scala b/src/dotty/tools/dotc/repl/ammonite/Ansi.scala deleted file mode 100644 index 37c4de7b5..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/Ansi.scala +++ /dev/null @@ -1,256 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite.terminal - -object Ansi { - - /** - * Represents a single, atomic ANSI escape sequence that results in a - * color, background or decoration being added to the output. - * - * @param escape the actual ANSI escape sequence corresponding to this Attr - */ - case class Attr private[Ansi](escape: Option[String], resetMask: Int, applyMask: Int) { - override def toString = escape.getOrElse("") + Console.RESET - def transform(state: Short) = ((state & ~resetMask) | applyMask).toShort - - def matches(state: Short) = (state & resetMask) == applyMask - def apply(s: Ansi.Str) = s.overlay(this, 0, s.length) - } - - object Attr { - val Reset = new Attr(Some(Console.RESET), Short.MaxValue, 0) - - /** - * Quickly convert string-colors into [[Ansi.Attr]]s - */ - val ParseMap = { - val pairs = for { - cat <- categories - color <- cat.all - str <- color.escape - } yield (str, color) - (pairs :+ (Console.RESET -> Reset)).toMap - } - } - - /** - * Represents a set of [[Ansi.Attr]]s all occupying the same bit-space - * in the state `Short` - */ - sealed abstract class Category() { - val mask: Int - val all: Seq[Attr] - lazy val bitsMap = all.map{ m => m.applyMask -> m}.toMap - def makeAttr(s: Option[String], applyMask: Int) = { - new Attr(s, mask, applyMask) - } - } - - object Color extends Category { - - val mask = 15 << 7 - val Reset = makeAttr(Some("\u001b[39m"), 0 << 7) - val Black = makeAttr(Some(Console.BLACK), 1 << 7) - val Red = makeAttr(Some(Console.RED), 2 << 7) - val Green = makeAttr(Some(Console.GREEN), 3 << 7) - val Yellow = makeAttr(Some(Console.YELLOW), 4 << 7) - val Blue = makeAttr(Some(Console.BLUE), 5 << 7) - val Magenta = makeAttr(Some(Console.MAGENTA), 6 << 7) - val Cyan = makeAttr(Some(Console.CYAN), 7 << 7) - val White = makeAttr(Some(Console.WHITE), 8 << 7) - - val all = Vector( - Reset, Black, Red, Green, Yellow, - Blue, Magenta, Cyan, White - ) - } - - object Back extends Category { - val mask = 15 << 3 - - val Reset = makeAttr(Some("\u001b[49m"), 0 << 3) - val Black = makeAttr(Some(Console.BLACK_B), 1 << 3) - val Red = makeAttr(Some(Console.RED_B), 2 << 3) - val Green = makeAttr(Some(Console.GREEN_B), 3 << 3) - val Yellow = makeAttr(Some(Console.YELLOW_B), 4 << 3) - val Blue = makeAttr(Some(Console.BLUE_B), 5 << 3) - val Magenta = makeAttr(Some(Console.MAGENTA_B), 6 << 3) - val Cyan = makeAttr(Some(Console.CYAN_B), 7 << 3) - val White = makeAttr(Some(Console.WHITE_B), 8 << 3) - - val all = Seq( - Reset, Black, Red, Green, Yellow, - Blue, Magenta, Cyan, White - ) - } - - object Bold extends Category { - val mask = 1 << 0 - val On = makeAttr(Some(Console.BOLD), 1 << 0) - val Off = makeAttr(None , 0 << 0) - val all = Seq(On, Off) - } - - object Underlined extends Category { - val mask = 1 << 1 - val On = makeAttr(Some(Console.UNDERLINED), 1 << 1) - val Off = makeAttr(None, 0 << 1) - val all = Seq(On, Off) - } - - object Reversed extends Category { - val mask = 1 << 2 - val On = makeAttr(Some(Console.REVERSED), 1 << 2) - val Off = makeAttr(None, 0 << 2) - val all = Seq(On, Off) - } - - val hardOffMask = Bold.mask | Underlined.mask | Reversed.mask - val categories = List(Color, Back, Bold, Underlined, Reversed) - - object Str { - @sharable lazy val ansiRegex = "\u001B\\[[;\\d]*m".r - - implicit def parse(raw: CharSequence): Str = { - val chars = new Array[Char](raw.length) - val colors = new Array[Short](raw.length) - var currentIndex = 0 - var currentColor = 0.toShort - - val matches = ansiRegex.findAllMatchIn(raw) - val indices = Seq(0) ++ matches.flatMap { m => Seq(m.start, m.end) } ++ Seq(raw.length) - - for { - Seq(start, end) <- indices.sliding(2).toSeq - if start != end - } { - val frag = raw.subSequence(start, end).toString - if (frag.charAt(0) == '\u001b' && Attr.ParseMap.contains(frag)) { - currentColor = Attr.ParseMap(frag).transform(currentColor) - } else { - var i = 0 - while(i < frag.length){ - chars(currentIndex) = frag(i) - colors(currentIndex) = currentColor - i += 1 - currentIndex += 1 - } - } - } - - Str(chars.take(currentIndex), colors.take(currentIndex)) - } - } - - /** - * An [[Ansi.Str]]'s `color`s array is filled with shorts, each representing - * the ANSI state of one character encoded in its bits. Each [[Attr]] belongs - * to a [[Category]] that occupies a range of bits within each short: - * - * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - * |-----------| |--------| |--------| | | |bold - * | | | | |reversed - * | | | |underlined - * | | |foreground-color - * | |background-color - * |unused - * - * - * The `0000 0000 0000 0000` short corresponds to plain text with no decoration - * - */ - type State = Short - - /** - * Encapsulates a string with associated ANSI colors and text decorations. - * - * Contains some basic string methods, as well as some ansi methods to e.g. - * apply particular colors or other decorations to particular sections of - * the [[Ansi.Str]]. [[render]] flattens it out into a `java.lang.String` - * with all the colors present as ANSI escapes. - * - */ - case class Str private(chars: Array[Char], colors: Array[State]) { - require(chars.length == colors.length) - - def ++(other: Str) = Str(chars ++ other.chars, colors ++ other.colors) - def splitAt(index: Int) = { - val (leftChars, rightChars) = chars.splitAt(index) - val (leftColors, rightColors) = colors.splitAt(index) - (new Str(leftChars, leftColors), new Str(rightChars, rightColors)) - } - - def length = chars.length - override def toString = render - - def plainText = new String(chars.toArray) - def render = { - // Pre-size StringBuilder with approximate size (ansi colors tend - // to be about 5 chars long) to avoid re-allocations during growth - val output = new StringBuilder(chars.length + colors.length * 5) - - - var currentState = 0.toShort - /** - * Emit the ansi escapes necessary to transition - * between two states, if necessary. - */ - def emitDiff(nextState: Short) = if (currentState != nextState){ - // Any of these transitions from 1 to 0 within the hardOffMask - // categories cannot be done with a single ansi escape, and need - // you to emit a RESET followed by re-building whatever ansi state - // you previous had from scratch - if ((currentState & ~nextState & hardOffMask) != 0){ - output.append(Console.RESET) - currentState = 0 - } - - var categoryIndex = 0 - while(categoryIndex < categories.length){ - val cat = categories(categoryIndex) - if ((cat.mask & currentState) != (cat.mask & nextState)){ - val attr = cat.bitsMap(nextState & cat.mask) - - if (attr.escape.isDefined) { - output.append(attr.escape.get) - } - } - categoryIndex += 1 - } - } - - var i = 0 - while(i < colors.length){ - // Emit ANSI escapes to change colors where necessary - emitDiff(colors(i)) - currentState = colors(i) - output.append(chars(i)) - i += 1 - } - - // Cap off the left-hand-side of the rendered string with any ansi escape - // codes necessary to rest the state to 0 - emitDiff(0) - output.toString - } - - /** - * Overlays the desired color over the specified range of the [[Ansi.Str]]. - */ - def overlay(overlayColor: Attr, start: Int, end: Int) = { - require(end >= start, - s"end:$end must be greater than start:$end in AnsiStr#overlay call" - ) - val colorsOut = new Array[Short](colors.length) - var i = 0 - while(i < colors.length){ - if (i >= start && i < end) colorsOut(i) = overlayColor.transform(colors(i)) - else colorsOut(i) = colors(i) - i += 1 - } - new Str(chars, colorsOut) - } - } -} diff --git a/src/dotty/tools/dotc/repl/ammonite/Filter.scala b/src/dotty/tools/dotc/repl/ammonite/Filter.scala deleted file mode 100644 index 9d34bb0f2..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/Filter.scala +++ /dev/null @@ -1,61 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite.terminal - -object Filter { - def apply(id: String)(f: PartialFunction[TermInfo, TermAction]): Filter = - new Filter { - val op = f.lift - def identifier = id - } - - def wrap(id: String)(f: TermInfo => Option[TermAction]): Filter = - new Filter { - val op = f - def identifier = id - } - - /** Merges multiple [[Filter]]s into one. */ - def merge(pfs: Filter*) = new Filter { - val op = (v1: TermInfo) => pfs.iterator.map(_.op(v1)).find(_.isDefined).flatten - def identifier = pfs.iterator.map(_.identifier).mkString(":") - } - - val empty = Filter.merge() -} - -/** - * The way you configure your terminal behavior; a trivial wrapper around a - * function, though you should provide a good `.toString` method to make - * debugging easier. The [[TermInfo]] and [[TermAction]] types are its - * interface to the terminal. - * - * [[Filter]]s are composed sequentially: if a filter returns `None` the next - * filter is tried, while if a filter returns `Some` that ends the cascade. - * While your `op` function interacts with the terminal purely through - * immutable case classes, the Filter itself is free to maintain its own state - * and mutate it whenever, even when returning `None` to continue the cascade. - */ -trait Filter { - val op: TermInfo => Option[TermAction] - - /** - * the `.toString` of this object, except by making it separate we force - * the implementer to provide something and stop them from accidentally - * leaving it as the meaningless default. - */ - def identifier: String - override def toString = identifier -} - -/** - * A filter as an abstract class, letting you provide a [[filter]] instead of - * an `op`, automatically providing a good `.toString` for debugging, and - * providing a reasonable "place" inside the inheriting class/object to put - * state or helpers or other logic associated with the filter. - */ -abstract class DelegateFilter() extends Filter { - def filter: Filter - val op = filter.op -} diff --git a/src/dotty/tools/dotc/repl/ammonite/FilterTools.scala b/src/dotty/tools/dotc/repl/ammonite/FilterTools.scala deleted file mode 100644 index c18b6a927..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/FilterTools.scala +++ /dev/null @@ -1,80 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite.terminal - -/** - * A collection of helpers that to simpify the common case of building filters - */ -object FilterTools { - val ansiRegex = "\u001B\\[[;\\d]*." - - def offsetIndex(buffer: Vector[Char], in: Int) = { - var splitIndex = 0 - var length = 0 - - while(length < in) { - ansiRegex.r.findPrefixOf(buffer.drop(splitIndex)) match { - case None => - splitIndex += 1 - length += 1 - case Some(s) => - splitIndex += s.length - } - } - splitIndex - } - - /** - * Shorthand to construct a filter in the common case where you're - * switching on the prefix of the input stream and want to run some - * transformation on the buffer/cursor - */ - def Case(s: String) - (f: (Vector[Char], Int, TermInfo) => (Vector[Char], Int)) = new Filter { - val op = new PartialFunction[TermInfo, TermAction] { - def isDefinedAt(x: TermInfo) = { - - def rec(i: Int, c: LazyList[Int]): Boolean = { - if (i >= s.length) true - else if (c.head == s(i)) rec(i + 1, c.tail) - else false - } - rec(0, x.ts.inputs) - } - - def apply(v1: TermInfo) = { - val (buffer1, cursor1) = f(v1.ts.buffer, v1.ts.cursor, v1) - TermState( - v1.ts.inputs.dropPrefix(s.map(_.toInt)).get, - buffer1, - cursor1 - ) - } - - }.lift - def identifier = "Case" - } - - /** Shorthand for pattern matching on [[TermState]] */ - val TS = TermState - - def findChunks(b: Vector[Char], c: Int) = { - val chunks = Terminal.splitBuffer(b) - // The index of the first character in each chunk - val chunkStarts = chunks.inits.map(x => x.length + x.sum).toStream.reverse - // Index of the current chunk that contains the cursor - val chunkIndex = chunkStarts.indexWhere(_ > c) match { - case -1 => chunks.length-1 - case x => x - 1 - } - (chunks, chunkStarts, chunkIndex) - } - - def firstRow(cursor: Int, buffer: Vector[Char], width: Int) = - cursor < width && (buffer.indexOf('\n') >= cursor || buffer.indexOf('\n') == -1) - - def lastRow(cursor: Int, buffer: Vector[Char], width: Int) = - (buffer.length - cursor) < width && - (buffer.lastIndexOf('\n') < cursor || buffer.lastIndexOf('\n') == -1) -} diff --git a/src/dotty/tools/dotc/repl/ammonite/LICENSE b/src/dotty/tools/dotc/repl/ammonite/LICENSE deleted file mode 100644 index b15103580..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -License -======= - - -The MIT License (MIT) - -Copyright (c) 2014 Li Haoyi (haoyi.sg@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE.
\ No newline at end of file diff --git a/src/dotty/tools/dotc/repl/ammonite/Protocol.scala b/src/dotty/tools/dotc/repl/ammonite/Protocol.scala deleted file mode 100644 index 34d31aeca..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/Protocol.scala +++ /dev/null @@ -1,30 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite.terminal - -case class TermInfo(ts: TermState, width: Int) - -sealed trait TermAction -case class Printing(ts: TermState, stdout: String) extends TermAction -case class TermState( - inputs: LazyList[Int], - buffer: Vector[Char], - cursor: Int, - msg: Ansi.Str = "" -) extends TermAction - -object TermState { - def unapply(ti: TermInfo): Option[(LazyList[Int], Vector[Char], Int, Ansi.Str)] = - TermState.unapply(ti.ts) - - def unapply(ti: TermAction): Option[(LazyList[Int], Vector[Char], Int, Ansi.Str)] = - ti match { - case ts: TermState => TermState.unapply(ts) - case _ => None - } -} - -case class ClearScreen(ts: TermState) extends TermAction -case object Exit extends TermAction -case class Result(s: String) extends TermAction diff --git a/src/dotty/tools/dotc/repl/ammonite/SpecialKeys.scala b/src/dotty/tools/dotc/repl/ammonite/SpecialKeys.scala deleted file mode 100644 index d834cc10b..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/SpecialKeys.scala +++ /dev/null @@ -1,81 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite.terminal - -/** - * One place to assign all the esotic control key input snippets to - * easy-to-remember names - */ -object SpecialKeys { - - /** - * Lets you easily pattern match on characters modified by ctrl, - * or convert a character into its ctrl-ed version - */ - object Ctrl { - def apply(c: Char) = (c - 96).toChar.toString - def unapply(i: Int): Option[Int] = Some(i + 96) - } - - /** - * The string value you get when you hit the alt key - */ - def Alt = "\u001b" - - - val Up = Alt+"[A" - val Down = Alt+"[B" - val Right = Alt+"[C" - val Left = Alt+"[D" - - val Home = Alt+"OH" - val End = Alt+"OF" - - // For some reason Screen makes these print different incantations - // from a normal snippet, so this causes issues like - // https://github.com/lihaoyi/Ammonite/issues/152 unless we special - // case them - val HomeScreen = Alt+"[1~" - val EndScreen = Alt+"[4~" - - val ShiftUp = Alt+"[1;2A" - val ShiftDown = Alt+"[1;2B" - val ShiftRight = Alt+"[1;2C" - val ShiftLeft = Alt+"[1;2D" - - val FnUp = Alt+"[5~" - val FnDown = Alt+"[6~" - val FnRight = Alt+"[F" - val FnLeft = Alt+"[H" - - val AltUp = Alt*2+"[A" - val AltDown = Alt*2+"[B" - val AltRight = Alt*2+"[C" - val AltLeft = Alt*2+"[D" - - val LinuxCtrlRight = Alt+"[1;5C" - val LinuxCtrlLeft = Alt+"[1;5D" - - val FnAltUp = Alt*2+"[5~" - val FnAltDown = Alt*2+"[6~" - val FnAltRight = Alt+"[1;9F" - val FnAltLeft = Alt+"[1;9H" - - // Same as fn-alt-{up, down} -// val FnShiftUp = Alt*2+"[5~" -// val FnShiftDown = Alt*2+"[6~" - val FnShiftRight = Alt+"[1;2F" - val FnShiftLeft = Alt+"[1;2H" - - val AltShiftUp = Alt+"[1;10A" - val AltShiftDown = Alt+"[1;10B" - val AltShiftRight = Alt+"[1;10C" - val AltShiftLeft = Alt+"[1;10D" - - // Same as fn-alt-{up, down} -// val FnAltShiftUp = Alt*2+"[5~" -// val FnAltShiftDown = Alt*2+"[6~" - val FnAltShiftRight = Alt+"[1;10F" - val FnAltShiftLeft = Alt+"[1;10H" -} diff --git a/src/dotty/tools/dotc/repl/ammonite/Terminal.scala b/src/dotty/tools/dotc/repl/ammonite/Terminal.scala deleted file mode 100644 index 4b18b38e3..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/Terminal.scala +++ /dev/null @@ -1,320 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite -package terminal - -import scala.annotation.tailrec -import scala.collection.mutable - -/** - * The core logic around a terminal; it defines the base `filters` API - * through which anything (including basic cursor-navigation and typing) - * interacts with the terminal. - * - * Maintains basic invariants, such as "cursor should always be within - * the buffer", and "ansi terminal should reflect most up to date TermState" - */ -object Terminal { - - /** - * Computes how tall a line of text is when wrapped at `width`. - * - * Even 0-character lines still take up one row! - * - * width = 2 - * 0 -> 1 - * 1 -> 1 - * 2 -> 1 - * 3 -> 2 - * 4 -> 2 - * 5 -> 3 - */ - def fragHeight(length: Int, width: Int) = math.max(1, (length - 1) / width + 1) - - def splitBuffer(buffer: Vector[Char]) = { - val frags = mutable.Buffer.empty[Int] - frags.append(0) - for(c <- buffer){ - if (c == '\n') frags.append(0) - else frags(frags.length - 1) = frags.last + 1 - } - frags - } - def calculateHeight(buffer: Vector[Char], - width: Int, - prompt: String): Seq[Int] = { - val rowLengths = splitBuffer(buffer) - - calculateHeight0(rowLengths, width - prompt.length) - } - - /** - * Given a buffer with characters and newlines, calculates how high - * the buffer is and where the cursor goes inside of it. - */ - def calculateHeight0(rowLengths: Seq[Int], - width: Int): Seq[Int] = { - val fragHeights = - rowLengths - .inits - .toVector - .reverse // We want shortest-to-longest, inits gives longest-to-shortest - .filter(_.nonEmpty) // Without the first empty prefix - .map{ x => - fragHeight( - // If the frag barely fits on one line, give it - // an extra spot for the cursor on the next line - x.last + 1, - width - ) - } -// Debug("fragHeights " + fragHeights) - fragHeights - } - - def positionCursor(cursor: Int, - rowLengths: Seq[Int], - fragHeights: Seq[Int], - width: Int) = { - var leftoverCursor = cursor - // Debug("leftoverCursor " + leftoverCursor) - var totalPreHeight = 0 - var done = false - // Don't check if the cursor exceeds the last chunk, because - // even if it does there's nowhere else for it to go - for(i <- 0 until rowLengths.length -1 if !done) { - // length of frag and the '\n' after it - val delta = rowLengths(i) + 1 - // Debug("delta " + delta) - val nextCursor = leftoverCursor - delta - if (nextCursor >= 0) { - // Debug("nextCursor " + nextCursor) - leftoverCursor = nextCursor - totalPreHeight += fragHeights(i) - }else done = true - } - - val cursorY = totalPreHeight + leftoverCursor / width - val cursorX = leftoverCursor % width - - (cursorY, cursorX) - } - - - type Action = (Vector[Char], Int) => (Vector[Char], Int) - type MsgAction = (Vector[Char], Int) => (Vector[Char], Int, String) - - - def noTransform(x: Vector[Char], i: Int) = (Ansi.Str.parse(x), i) - /** - * Blockingly reads a line from the given input stream and returns it. - * - * @param prompt The prompt to display when requesting input - * @param reader The input-stream where characters come in, e.g. System.in - * @param writer The output-stream where print-outs go, e.g. System.out - * @param filters A set of actions that can be taken depending on the input, - * @param displayTransform code to manipulate the display of the buffer and - * cursor, without actually changing the logical - * values inside them. - */ - def readLine(prompt: Prompt, - reader: java.io.Reader, - writer: java.io.Writer, - filters: Filter, - displayTransform: (Vector[Char], Int) => (Ansi.Str, Int) = noTransform) - : Option[String] = { - - /** - * Erases the previous line and re-draws it with the new buffer and - * cursor. - * - * Relies on `ups` to know how "tall" the previous line was, to go up - * and erase that many rows in the console. Performs a lot of horrific - * math all over the place, incredibly prone to off-by-ones, in order - * to at the end of the day position the cursor in the right spot. - */ - def redrawLine(buffer: Ansi.Str, - cursor: Int, - ups: Int, - rowLengths: Seq[Int], - fullPrompt: Boolean = true, - newlinePrompt: Boolean = false) = { - - - // Enable this in certain cases (e.g. cursor near the value you are - // interested into) see what's going on with all the ansi screen-cursor - // movement - def debugDelay() = if (false){ - Thread.sleep(200) - writer.flush() - } - - - val promptLine = - if (fullPrompt) prompt.full - else prompt.lastLine - - val promptWidth = if(newlinePrompt) 0 else prompt.lastLine.length - val actualWidth = width - promptWidth - - ansi.up(ups) - ansi.left(9999) - ansi.clearScreen(0) - writer.write(promptLine.toString) - if (newlinePrompt) writer.write("\n") - - // I'm not sure why this is necessary, but it seems that without it, a - // cursor that "barely" overshoots the end of a line, at the end of the - // buffer, does not properly wrap and ends up dangling off the - // right-edge of the terminal window! - // - // This causes problems later since the cursor is at the wrong X/Y, - // confusing the rest of the math and ending up over-shooting on the - // `ansi.up` calls, over-writing earlier lines. This prints a single - // space such that instead of dangling it forces the cursor onto the - // next line for-realz. If it isn't dangling the extra space is a no-op - val lineStuffer = ' ' - // Under `newlinePrompt`, we print the thing almost-verbatim, since we - // want to avoid breaking code by adding random indentation. If not, we - // are guaranteed that the lines are short, so we can indent the newlines - // without fear of wrapping - val newlineReplacement = - if (newlinePrompt) { - - Array(lineStuffer, '\n') - } else { - val indent = " " * prompt.lastLine.length - Array('\n', indent:_*) - } - - writer.write( - buffer.render.flatMap{ - case '\n' => newlineReplacement - case x => Array(x) - }.toArray - ) - writer.write(lineStuffer) - - val fragHeights = calculateHeight0(rowLengths, actualWidth) - val (cursorY, cursorX) = positionCursor( - cursor, - rowLengths, - fragHeights, - actualWidth - ) - ansi.up(fragHeights.sum - 1) - ansi.left(9999) - ansi.down(cursorY) - ansi.right(cursorX) - if (!newlinePrompt) ansi.right(prompt.lastLine.length) - - writer.flush() - } - - @tailrec - def readChar(lastState: TermState, ups: Int, fullPrompt: Boolean = true): Option[String] = { - val moreInputComing = reader.ready() - - lazy val (transformedBuffer0, cursorOffset) = displayTransform( - lastState.buffer, - lastState.cursor - ) - - lazy val transformedBuffer = transformedBuffer0 ++ lastState.msg - lazy val lastOffsetCursor = lastState.cursor + cursorOffset - lazy val rowLengths = splitBuffer( - lastState.buffer ++ lastState.msg.plainText - ) - val narrowWidth = width - prompt.lastLine.length - val newlinePrompt = rowLengths.exists(_ >= narrowWidth) - val promptWidth = if(newlinePrompt) 0 else prompt.lastLine.length - val actualWidth = width - promptWidth - val newlineUp = if (newlinePrompt) 1 else 0 - if (!moreInputComing) redrawLine( - transformedBuffer, - lastOffsetCursor, - ups, - rowLengths, - fullPrompt, - newlinePrompt - ) - - lazy val (oldCursorY, _) = positionCursor( - lastOffsetCursor, - rowLengths, - calculateHeight0(rowLengths, actualWidth), - actualWidth - ) - - def updateState(s: LazyList[Int], - b: Vector[Char], - c: Int, - msg: Ansi.Str): (Int, TermState) = { - - val newCursor = math.max(math.min(c, b.length), 0) - val nextUps = - if (moreInputComing) ups - else oldCursorY + newlineUp - - val newState = TermState(s, b, newCursor, msg) - - (nextUps, newState) - } - // `.get` because we assume that *some* filter is going to match each - // character, even if only to dump the character to the screen. If nobody - // matches the character then we can feel free to blow up - filters.op(TermInfo(lastState, actualWidth)).get match { - case Printing(TermState(s, b, c, msg), stdout) => - writer.write(stdout) - val (nextUps, newState) = updateState(s, b, c, msg) - readChar(newState, nextUps) - - case TermState(s, b, c, msg) => - val (nextUps, newState) = updateState(s, b, c, msg) - readChar(newState, nextUps, false) - - case Result(s) => - redrawLine( - transformedBuffer, lastState.buffer.length, - oldCursorY + newlineUp, rowLengths, false, newlinePrompt - ) - writer.write(10) - writer.write(13) - writer.flush() - Some(s) - case ClearScreen(ts) => - ansi.clearScreen(2) - ansi.up(9999) - ansi.left(9999) - readChar(ts, ups) - case Exit => - None - } - } - - lazy val ansi = new AnsiNav(writer) - lazy val (width, _, initialConfig) = TTY.init() - try { - readChar(TermState(LazyList.continually(reader.read()), Vector.empty, 0, ""), 0) - }finally{ - - // Don't close these! Closing these closes stdin/stdout, - // which seems to kill the entire program - - // reader.close() - // writer.close() - TTY.stty(initialConfig) - } - } -} -object Prompt { - implicit def construct(prompt: String): Prompt = { - val parsedPrompt = Ansi.Str.parse(prompt) - val index = parsedPrompt.plainText.lastIndexOf('\n') - val (_, last) = parsedPrompt.splitAt(index+1) - Prompt(parsedPrompt, last) - } -} - -case class Prompt(full: Ansi.Str, lastLine: Ansi.Str) diff --git a/src/dotty/tools/dotc/repl/ammonite/Utils.scala b/src/dotty/tools/dotc/repl/ammonite/Utils.scala deleted file mode 100644 index 64a2c1476..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/Utils.scala +++ /dev/null @@ -1,169 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite.terminal - -import java.io.{FileOutputStream, Writer, File => JFile} -import scala.annotation.tailrec - -/** - * Prints stuff to an ad-hoc logging file when running the repl or terminal in - * development mode - * - * Very handy for the common case where you're debugging terminal interactions - * and cannot use `println` because it will stomp all over your already messed - * up terminal state and block debugging. With [[Debug]], you can have a - * separate terminal open tailing the log file and log as verbosely as you - * want without affecting the primary terminal you're using to interact with - * Ammonite. - */ -object Debug { - lazy val debugOutput = - new FileOutputStream(new JFile("terminal/target/log")) - - def apply(s: Any) = - if (System.getProperty("ammonite-sbt-build") == "true") - debugOutput.write((System.currentTimeMillis() + "\t\t" + s + "\n").getBytes) -} - -class AnsiNav(output: Writer) { - def control(n: Int, c: Char) = output.write(s"\033[" + n + c) - - /** - * Move up `n` squares - */ - def up(n: Int) = if (n == 0) "" else control(n, 'A') - /** - * Move down `n` squares - */ - def down(n: Int) = if (n == 0) "" else control(n, 'B') - /** - * Move right `n` squares - */ - def right(n: Int) = if (n == 0) "" else control(n, 'C') - /** - * Move left `n` squares - */ - def left(n: Int) = if (n == 0) "" else control(n, 'D') - - /** - * Clear the screen - * - * n=0: clear from cursor to end of screen - * n=1: clear from cursor to start of screen - * n=2: clear entire screen - */ - def clearScreen(n: Int) = control(n, 'J') - /** - * Clear the current line - * - * n=0: clear from cursor to end of line - * n=1: clear from cursor to start of line - * n=2: clear entire line - */ - def clearLine(n: Int) = control(n, 'K') -} - -object AnsiNav { - val resetUnderline = "\u001b[24m" - val resetForegroundColor = "\u001b[39m" - val resetBackgroundColor = "\u001b[49m" -} - -object TTY { - - // Prefer standard tools. Not sure why we need to do this, but for some - // reason the version installed by gnu-coreutils blows up sometimes giving - // "unable to perform all requested operations" - val pathedTput = if (new java.io.File("/usr/bin/tput").exists()) "/usr/bin/tput" else "tput" - val pathedStty = if (new java.io.File("/bin/stty").exists()) "/bin/stty" else "stty" - - def consoleDim(s: String) = { - import sys.process._ - Seq("bash", "-c", s"$pathedTput $s 2> /dev/tty").!!.trim.toInt - } - def init() = { - stty("-a") - - val width = consoleDim("cols") - val height = consoleDim("lines") -// Debug("Initializing, Width " + width) -// Debug("Initializing, Height " + height) - val initialConfig = stty("-g").trim - stty("-icanon min 1 -icrnl -inlcr -ixon") - sttyFailTolerant("dsusp undef") - stty("-echo") - stty("intr undef") -// Debug("") - (width, height, initialConfig) - } - - private def sttyCmd(s: String) = { - import sys.process._ - Seq("bash", "-c", s"$pathedStty $s < /dev/tty"): ProcessBuilder - } - - def stty(s: String) = - sttyCmd(s).!! - /* - * Executes a stty command for which failure is expected, hence the return - * status can be non-null and errors are ignored. - * This is appropriate for `stty dsusp undef`, since it's unsupported on Linux - * (http://man7.org/linux/man-pages/man3/termios.3.html). - */ - def sttyFailTolerant(s: String) = - sttyCmd(s ++ " 2> /dev/null").! - - def restore(initialConfig: String) = { - stty(initialConfig) - } -} - -/** - * A truly-lazy implementation of scala.Stream - */ -case class LazyList[T](headThunk: () => T, tailThunk: () => LazyList[T]) { - var rendered = false - lazy val head = { - rendered = true - headThunk() - } - - lazy val tail = tailThunk() - - def dropPrefix(prefix: Seq[T]) = { - @tailrec def rec(n: Int, l: LazyList[T]): Option[LazyList[T]] = { - if (n >= prefix.length) Some(l) - else if (prefix(n) == l.head) rec(n + 1, l.tail) - else None - } - rec(0, this) - } - override def toString = { - - @tailrec def rec(l: LazyList[T], res: List[T]): List[T] = { - if (l.rendered) rec(l.tailThunk(), l.head :: res) - else res - } - s"LazyList(${(rec(this, Nil).reverse ++ Seq("...")).mkString(",")})" - } - - def ~:(other: => T) = LazyList(() => other, () => this) -} - -object LazyList { - object ~: { - def unapply[T](x: LazyList[T]) = Some((x.head, x.tail)) - } - - def continually[T](t: => T): LazyList[T] = LazyList(() => t, () =>continually(t)) - - implicit class CS(ctx: StringContext) { - val base = ctx.parts.mkString - object p { - def unapply(s: LazyList[Int]): Option[LazyList[Int]] = { - s.dropPrefix(base.map(_.toInt)) - } - } - } -} diff --git a/src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala b/src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala deleted file mode 100644 index faa97c348..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala +++ /dev/null @@ -1,163 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite -package terminal -package filters - -import ammonite.terminal.FilterTools._ -import ammonite.terminal.LazyList._ -import ammonite.terminal.SpecialKeys._ -import ammonite.terminal.Filter -import ammonite.terminal._ - -/** - * Filters for simple operation of a terminal: cursor-navigation - * (including with all the modifier keys), enter/ctrl-c-exit, etc. - */ -object BasicFilters { - def all = Filter.merge( - navFilter, - exitFilter, - enterFilter, - clearFilter, - //loggingFilter, - typingFilter - ) - - def injectNewLine(b: Vector[Char], c: Int, rest: LazyList[Int], indent: Int = 0) = { - val (first, last) = b.splitAt(c) - TermState(rest, (first :+ '\n') ++ last ++ Vector.fill(indent)(' '), c + 1 + indent) - } - - def navFilter = Filter.merge( - Case(Up)((b, c, m) => moveUp(b, c, m.width)), - Case(Down)((b, c, m) => moveDown(b, c, m.width)), - Case(Right)((b, c, m) => (b, c + 1)), - Case(Left)((b, c, m) => (b, c - 1)) - ) - - def tabColumn(indent: Int, b: Vector[Char], c: Int, rest: LazyList[Int]) = { - val (chunks, chunkStarts, chunkIndex) = FilterTools.findChunks(b, c) - val chunkCol = c - chunkStarts(chunkIndex) - val spacesToInject = indent - (chunkCol % indent) - val (lhs, rhs) = b.splitAt(c) - TS(rest, lhs ++ Vector.fill(spacesToInject)(' ') ++ rhs, c + spacesToInject) - } - - def tabFilter(indent: Int): Filter = Filter("tabFilter") { - case TS(9 ~: rest, b, c, _) => tabColumn(indent, b, c, rest) - } - - def loggingFilter: Filter = Filter("loggingFilter") { - case TS(Ctrl('q') ~: rest, b, c, _) => - println("Char Display Mode Enabled! Ctrl-C to exit") - var curr = rest - while (curr.head != 3) { - println("Char " + curr.head) - curr = curr.tail - } - TS(curr, b, c) - } - - def typingFilter: Filter = Filter("typingFilter") { - case TS(p"\u001b[3~$rest", b, c, _) => -// Debug("fn-delete") - val (first, last) = b.splitAt(c) - TS(rest, first ++ last.drop(1), c) - - case TS(127 ~: rest, b, c, _) => // Backspace - val (first, last) = b.splitAt(c) - TS(rest, first.dropRight(1) ++ last, c - 1) - - case TS(char ~: rest, b, c, _) => -// Debug("NORMAL CHAR " + char) - val (first, last) = b.splitAt(c) - TS(rest, (first :+ char.toChar) ++ last, c + 1) - } - - def doEnter(b: Vector[Char], c: Int, rest: LazyList[Int]) = { - val (chunks, chunkStarts, chunkIndex) = FilterTools.findChunks(b, c) - if (chunkIndex == chunks.length - 1) Result(b.mkString) - else injectNewLine(b, c, rest) - } - - def enterFilter: Filter = Filter("enterFilter") { - case TS(13 ~: rest, b, c, _) => doEnter(b, c, rest) // Enter - case TS(10 ~: rest, b, c, _) => doEnter(b, c, rest) // Enter - case TS(10 ~: 13 ~: rest, b, c, _) => doEnter(b, c, rest) // Enter - case TS(13 ~: 10 ~: rest, b, c, _) => doEnter(b, c, rest) // Enter - } - - def exitFilter: Filter = Filter("exitFilter") { - case TS(Ctrl('c') ~: rest, b, c, _) => - Result("") - case TS(Ctrl('d') ~: rest, b, c, _) => - // only exit if the line is empty, otherwise, behave like - // "delete" (i.e. delete one char to the right) - if (b.isEmpty) Exit else { - val (first, last) = b.splitAt(c) - TS(rest, first ++ last.drop(1), c) - } - case TS(-1 ~: rest, b, c, _) => Exit // java.io.Reader.read() produces -1 on EOF - } - - def clearFilter: Filter = Filter("clearFilter") { - case TS(Ctrl('l') ~: rest, b, c, _) => ClearScreen(TS(rest, b, c)) - } - - def moveStart(b: Vector[Char], c: Int, w: Int) = { - val (_, chunkStarts, chunkIndex) = findChunks(b, c) - val currentColumn = (c - chunkStarts(chunkIndex)) % w - b -> (c - currentColumn) - } - - def moveEnd(b: Vector[Char], c: Int, w: Int) = { - val (chunks, chunkStarts, chunkIndex) = findChunks(b, c) - val currentColumn = (c - chunkStarts(chunkIndex)) % w - val c1 = chunks.lift(chunkIndex + 1) match { - case Some(next) => - val boundary = chunkStarts(chunkIndex + 1) - 1 - if ((boundary - c) > (w - currentColumn)) { - val delta= w - currentColumn - c + delta - } - else boundary - case None => - c + 1 * 9999 - } - b -> c1 - } - - def moveUpDown( - b: Vector[Char], - c: Int, - w: Int, - boundaryOffset: Int, - nextChunkOffset: Int, - checkRes: Int, - check: (Int, Int) => Boolean, - isDown: Boolean - ) = { - val (chunks, chunkStarts, chunkIndex) = findChunks(b, c) - val offset = chunkStarts(chunkIndex + boundaryOffset) - if (check(checkRes, offset)) checkRes - else chunks.lift(chunkIndex + nextChunkOffset) match { - case None => c + nextChunkOffset * 9999 - case Some(next) => - val boundary = chunkStarts(chunkIndex + boundaryOffset) - val currentColumn = (c - chunkStarts(chunkIndex)) % w - - if (isDown) boundary + math.min(currentColumn, next) - else boundary + math.min(currentColumn - next % w, 0) - 1 - } - } - - def moveUp(b: Vector[Char], c: Int, w: Int) = { - b -> moveUpDown(b, c, w, 0, -1, c - w, _ > _, false) - } - - def moveDown(b: Vector[Char], c: Int, w: Int) = { - b -> moveUpDown(b, c, w, 1, 1, c + w, _ <= _, true) - } -} diff --git a/src/dotty/tools/dotc/repl/ammonite/filters/GUILikeFilters.scala b/src/dotty/tools/dotc/repl/ammonite/filters/GUILikeFilters.scala deleted file mode 100644 index 69a9769c6..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/filters/GUILikeFilters.scala +++ /dev/null @@ -1,170 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite -package terminal -package filters - -import terminal.FilterTools._ -import terminal.LazyList.~: -import terminal.SpecialKeys._ -import terminal.DelegateFilter -import terminal._ - -/** - * Filters have hook into the various {Ctrl,Shift,Fn,Alt}x{Up,Down,Left,Right} - * combination keys, and make them behave similarly as they would on a normal - * GUI text editor: alt-{left, right} for word movement, hold-down-shift for - * text selection, etc. - */ -object GUILikeFilters { - case class SelectionFilter(indent: Int) extends DelegateFilter { - def identifier = "SelectionFilter" - var mark: Option[Int] = None - - def setMark(c: Int) = { - Debug("setMark\t" + mark + "\t->\t" + c) - if (mark == None) mark = Some(c) - } - - def doIndent( - b: Vector[Char], - c: Int, - rest: LazyList[Int], - slicer: Vector[Char] => Int - ) = { - - val markValue = mark.get - val (chunks, chunkStarts, chunkIndex) = FilterTools.findChunks(b, c) - val min = chunkStarts.lastIndexWhere(_ <= math.min(c, markValue)) - val max = chunkStarts.indexWhere(_ > math.max(c, markValue)) - val splitPoints = chunkStarts.slice(min, max) - val frags = (0 +: splitPoints :+ 99999).sliding(2).zipWithIndex - - var firstOffset = 0 - val broken = - for((Seq(l, r), i) <- frags) yield { - val slice = b.slice(l, r) - if (i == 0) slice - else { - val cut = slicer(slice) - - if (i == 1) firstOffset = cut - - if (cut < 0) slice.drop(-cut) - else Vector.fill(cut)(' ') ++ slice - } - } - val flattened = broken.flatten.toVector - val deeperOffset = flattened.length - b.length - - val (newMark, newC) = - if (mark.get > c) (mark.get + deeperOffset, c + firstOffset) - else (mark.get + firstOffset, c + deeperOffset) - - mark = Some(newMark) - TS(rest, flattened, newC) - } - - def filter = Filter.merge( - - Case(ShiftUp) {(b, c, m) => setMark(c); BasicFilters.moveUp(b, c, m.width)}, - Case(ShiftDown) {(b, c, m) => setMark(c); BasicFilters.moveDown(b, c, m.width)}, - Case(ShiftRight) {(b, c, m) => setMark(c); (b, c + 1)}, - Case(ShiftLeft) {(b, c, m) => setMark(c); (b, c - 1)}, - Case(AltShiftUp) {(b, c, m) => setMark(c); BasicFilters.moveUp(b, c, m.width)}, - Case(AltShiftDown) {(b, c, m) => setMark(c); BasicFilters.moveDown(b, c, m.width)}, - Case(AltShiftRight) {(b, c, m) => setMark(c); wordRight(b, c)}, - Case(AltShiftLeft) {(b, c, m) => setMark(c); wordLeft(b, c)}, - Case(FnShiftRight) {(b, c, m) => setMark(c); BasicFilters.moveEnd(b, c, m.width)}, - Case(FnShiftLeft) {(b, c, m) => setMark(c); BasicFilters.moveStart(b, c, m.width)}, - Filter("fnOtherFilter") { - case TS(27 ~: 91 ~: 90 ~: rest, b, c, _) if mark.isDefined => - doIndent(b, c, rest, - slice => -math.min(slice.iterator.takeWhile(_ == ' ').size, indent) - ) - - case TS(9 ~: rest, b, c, _) if mark.isDefined => // Tab - doIndent(b, c, rest, - slice => indent - ) - - // Intercept every other character. - case TS(char ~: inputs, buffer, cursor, _) if mark.isDefined => - // If it's a special command, just cancel the current selection. - if (char.toChar.isControl && - char != 127 /*backspace*/ && - char != 13 /*enter*/ && - char != 10 /*enter*/) { - mark = None - TS(char ~: inputs, buffer, cursor) - } else { - // If it's a printable character, delete the current - // selection and write the printable character. - val Seq(min, max) = Seq(mark.get, cursor).sorted - mark = None - val newBuffer = buffer.take(min) ++ buffer.drop(max) - val newInputs = - if (char == 127) inputs - else char ~: inputs - TS(newInputs, newBuffer, min) - } - } - ) - } - - object SelectionFilter { - def mangleBuffer( - selectionFilter: SelectionFilter, - string: Ansi.Str, - cursor: Int, - startColor: Ansi.Attr - ) = { - selectionFilter.mark match { - case Some(mark) if mark != cursor => - val Seq(min, max) = Seq(cursor, mark).sorted - val displayOffset = if (cursor < mark) 0 else -1 - val newStr = string.overlay(startColor, min, max) - (newStr, displayOffset) - case _ => (string, 0) - } - } - } - - val fnFilter = Filter.merge( - Case(FnUp)((b, c, m) => (b, c - 9999)), - Case(FnDown)((b, c, m) => (b, c + 9999)), - Case(FnRight)((b, c, m) => BasicFilters.moveEnd(b, c, m.width)), - Case(FnLeft)((b, c, m) => BasicFilters.moveStart(b, c, m.width)) - ) - val altFilter = Filter.merge( - Case(AltUp) {(b, c, m) => BasicFilters.moveUp(b, c, m.width)}, - Case(AltDown) {(b, c, m) => BasicFilters.moveDown(b, c, m.width)}, - Case(AltRight) {(b, c, m) => wordRight(b, c)}, - Case(AltLeft) {(b, c, m) => wordLeft(b, c)} - ) - - val fnAltFilter = Filter.merge( - Case(FnAltUp) {(b, c, m) => (b, c)}, - Case(FnAltDown) {(b, c, m) => (b, c)}, - Case(FnAltRight) {(b, c, m) => (b, c)}, - Case(FnAltLeft) {(b, c, m) => (b, c)} - ) - val fnAltShiftFilter = Filter.merge( - Case(FnAltShiftRight) {(b, c, m) => (b, c)}, - Case(FnAltShiftLeft) {(b, c, m) => (b, c)} - ) - - - def consumeWord(b: Vector[Char], c: Int, delta: Int, offset: Int) = { - var current = c - while(b.isDefinedAt(current) && !b(current).isLetterOrDigit) current += delta - while(b.isDefinedAt(current) && b(current).isLetterOrDigit) current += delta - current + offset - } - - // c -1 to move at least one character! Otherwise you get stuck at the start of - // a word. - def wordLeft(b: Vector[Char], c: Int) = b -> consumeWord(b, c - 1, -1, 1) - def wordRight(b: Vector[Char], c: Int) = b -> consumeWord(b, c, 1, 0) -} diff --git a/src/dotty/tools/dotc/repl/ammonite/filters/HistoryFilter.scala b/src/dotty/tools/dotc/repl/ammonite/filters/HistoryFilter.scala deleted file mode 100644 index dac1c9d23..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/filters/HistoryFilter.scala +++ /dev/null @@ -1,334 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite -package terminal -package filters - -import terminal.FilterTools._ -import terminal.LazyList._ -import terminal._ - -/** - * Provides history navigation up and down, saving the current line, a well - * as history-search functionality (`Ctrl R` in bash) letting you quickly find - * & filter previous commands by entering a sub-string. - */ -class HistoryFilter( - history: () => IndexedSeq[String], - commentStartColor: String, - commentEndColor: String -) extends DelegateFilter { - - - def identifier = "HistoryFilter" - /** - * `-1` means we haven't started looking at history, `n >= 0` means we're - * currently at history command `n` - */ - var historyIndex = -1 - - /** - * The term we're searching for, if any. - * - * - `None` means we're not searching for anything, e.g. we're just - * browsing history - * - * - `Some(term)` where `term` is not empty is what it normally looks - * like when we're searching for something - * - * - `Some(term)` where `term` is empty only really happens when you - * start searching and delete things, or if you `Ctrl-R` on an empty - * prompt - */ - var searchTerm: Option[Vector[Char]] = None - - /** - * Records the last buffer that the filter has observed while it's in - * search/history mode. If the new buffer differs from this, assume that - * some other filter modified the buffer and drop out of search/history - */ - var prevBuffer: Option[Vector[Char]] = None - - /** - * Kicks the HistoryFilter from passive-mode into search-history mode - */ - def startHistory(b: Vector[Char], c: Int): (Vector[Char], Int, String) = { - if (b.nonEmpty) searchTerm = Some(b) - up(Vector(), c) - } - - def searchHistory( - start: Int, - increment: Int, - buffer: Vector[Char], - skipped: Vector[Char] - ) = { - - def nextHistoryIndexFor(v: Vector[Char]) = { - HistoryFilter.findNewHistoryIndex(start, v, history(), increment, skipped) - } - - val (newHistoryIndex, newBuffer, newMsg, newCursor) = searchTerm match { - // We're not searching for anything, just browsing history. - // Pass in Vector.empty so we scroll through all items - case None => - val (i, b, c) = nextHistoryIndexFor(Vector.empty) - (i, b, "", 99999) - - // We're searching for some item with a particular search term - case Some(b) if b.nonEmpty => - val (i, b1, c) = nextHistoryIndexFor(b) - - val msg = - if (i.nonEmpty) "" - else commentStartColor + HistoryFilter.cannotFindSearchMessage + commentEndColor - - (i, b1, msg, c) - - // We're searching for nothing in particular; in this case, - // show a help message instead of an unhelpful, empty buffer - case Some(b) if b.isEmpty => - val msg = commentStartColor + HistoryFilter.emptySearchMessage + commentEndColor - // The cursor in this case always goes to zero - (Some(start), Vector(), msg, 0) - - } - - historyIndex = newHistoryIndex.getOrElse(-1) - - (newBuffer, newCursor, newMsg) - } - - def activeHistory = searchTerm.nonEmpty || historyIndex != -1 - def activeSearch = searchTerm.nonEmpty - - def up(b: Vector[Char], c: Int) = - searchHistory(historyIndex + 1, 1, b, b) - - def down(b: Vector[Char], c: Int) = - searchHistory(historyIndex - 1, -1, b, b) - - def wrap(rest: LazyList[Int], out: (Vector[Char], Int, String)) = - TS(rest, out._1, out._2, out._3) - - def ctrlR(b: Vector[Char], c: Int) = - if (activeSearch) up(b, c) - else { - searchTerm = Some(b) - up(Vector(), c) - } - - def printableChar(char: Char)(b: Vector[Char], c: Int) = { - searchTerm = searchTerm.map(_ :+ char) - searchHistory(historyIndex.max(0), 1, b :+ char, Vector()) - } - - def backspace(b: Vector[Char], c: Int) = { - searchTerm = searchTerm.map(_.dropRight(1)) - searchHistory(historyIndex, 1, b, Vector()) - } - - /** - * Predicate to check if either we're searching for a term or if we're in - * history-browsing mode and some predicate is true. - * - * Very often we want to capture keystrokes in search-mode more aggressively - * than in history-mode, e.g. search-mode drops you out more aggressively - * than history-mode does, and its up/down keys cycle through history more - * aggressively on every keystroke while history-mode only cycles when you - * reach the top/bottom line of the multi-line input. - */ - def searchOrHistoryAnd(cond: Boolean) = - activeSearch || (activeHistory && cond) - - val dropHistoryChars = Set(9, 13, 10) // Tab or Enter - - def endHistory() = { - historyIndex = -1 - searchTerm = None - } - - def filter = Filter.wrap("historyFilterWrap1") { - (ti: TermInfo) => { - prelude.op(ti) match { - case None => - prevBuffer = Some(ti.ts.buffer) - filter0.op(ti) match { - case Some(ts: TermState) => - prevBuffer = Some(ts.buffer) - Some(ts) - case x => x - } - case some => some - } - } - } - - def prelude: Filter = Filter("historyPrelude") { - case TS(inputs, b, c, _) if activeHistory && prevBuffer.exists(_ != b) => - endHistory() - prevBuffer = None - TS(inputs, b, c) - } - - def filter0: Filter = Filter("filter0") { - // Ways to kick off the history/search if you're not already in it - - // `Ctrl-R` - case TS(18 ~: rest, b, c, _) => wrap(rest, ctrlR(b, c)) - - // `Up` from the first line in the input - case TermInfo(TS(p"\u001b[A$rest", b, c, _), w) if firstRow(c, b, w) && !activeHistory => - wrap(rest, startHistory(b, c)) - - // `Ctrl P` - case TermInfo(TS(p"\u0010$rest", b, c, _), w) if firstRow(c, b, w) && !activeHistory => - wrap(rest, startHistory(b, c)) - - // `Page-Up` from first character starts history - case TermInfo(TS(p"\u001b[5~$rest", b, c, _), w) if c == 0 && !activeHistory => - wrap(rest, startHistory(b, c)) - - // Things you can do when you're already in the history search - - // Navigating up and down the history. Each up or down searches for - // the next thing that matches your current searchTerm - // Up - case TermInfo(TS(p"\u001b[A$rest", b, c, _), w) if searchOrHistoryAnd(firstRow(c, b, w)) => - wrap(rest, up(b, c)) - - // Ctrl P - case TermInfo(TS(p"\u0010$rest", b, c, _), w) if searchOrHistoryAnd(firstRow(c, b, w)) => - wrap(rest, up(b, c)) - - // `Page-Up` from first character cycles history up - case TermInfo(TS(p"\u001b[5~$rest", b, c, _), w) if searchOrHistoryAnd(c == 0) => - wrap(rest, up(b, c)) - - // Down - case TermInfo(TS(p"\u001b[B$rest", b, c, _), w) if searchOrHistoryAnd(lastRow(c, b, w)) => - wrap(rest, down(b, c)) - - // `Ctrl N` - - case TermInfo(TS(p"\u000e$rest", b, c, _), w) if searchOrHistoryAnd(lastRow(c, b, w)) => - wrap(rest, down(b, c)) - // `Page-Down` from last character cycles history down - case TermInfo(TS(p"\u001b[6~$rest", b, c, _), w) if searchOrHistoryAnd(c == b.length - 1) => - wrap(rest, down(b, c)) - - - // Intercept Backspace and delete a character in search-mode, preserving it, but - // letting it fall through and dropping you out of history-mode if you try to make - // edits - case TS(127 ~: rest, buffer, cursor, _) if activeSearch => - wrap(rest, backspace(buffer, cursor)) - - // Any other control characters drop you out of search mode, but only the - // set of `dropHistoryChars` drops you out of history mode - case TS(char ~: inputs, buffer, cursor, _) - if char.toChar.isControl && searchOrHistoryAnd(dropHistoryChars(char)) => - val newBuffer = - // If we're back to -1, it means we've wrapped around and are - // displaying the original search term with a wrap-around message - // in the terminal. Drop the message and just preserve the search term - if (historyIndex == -1) searchTerm.get - // If we're searching for an empty string, special-case this and return - // an empty buffer rather than the first history item (which would be - // the default) because that wouldn't make much sense - else if (searchTerm.exists(_.isEmpty)) Vector() - // Otherwise, pick whatever history entry we're at and use that - else history()(historyIndex).toVector - endHistory() - - TS(char ~: inputs, newBuffer, cursor) - - // Intercept every other printable character when search is on and - // enter it into the current search - case TS(char ~: rest, buffer, cursor, _) if activeSearch => - wrap(rest, printableChar(char.toChar)(buffer, cursor)) - - // If you're not in search but are in history, entering any printable - // characters kicks you out of it and preserves the current buffer. This - // makes it harder for you to accidentally lose work due to history-moves - case TS(char ~: rest, buffer, cursor, _) if activeHistory && !char.toChar.isControl => - historyIndex = -1 - TS(char ~: rest, buffer, cursor) - } -} - -object HistoryFilter { - - def mangleBuffer( - historyFilter: HistoryFilter, - buffer: Ansi.Str, - cursor: Int, - startColor: Ansi.Attr - ) = { - if (!historyFilter.activeSearch) buffer - else { - val (searchStart, searchEnd) = - if (historyFilter.searchTerm.get.isEmpty) (cursor, cursor+1) - else { - val start = buffer.plainText.indexOfSlice(historyFilter.searchTerm.get) - - val end = start + (historyFilter.searchTerm.get.length max 1) - (start, end) - } - - val newStr = buffer.overlay(startColor, searchStart, searchEnd) - newStr - } - } - - /** - * @param startIndex The first index to start looking from - * @param searchTerm The term we're searching from; can be empty - * @param history The history we're searching through - * @param indexIncrement Which direction to search, +1 or -1 - * @param skipped Any buffers which we should skip in our search results, - * e.g. because the user has seen them before. - */ - def findNewHistoryIndex( - startIndex: Int, - searchTerm: Vector[Char], - history: IndexedSeq[String], - indexIncrement: Int, - skipped: Vector[Char] - ) = { - /** - * `Some(i)` means we found a reasonable result at history element `i` - * `None` means we couldn't find anything, and should show a not-found - * error to the user - */ - def rec(i: Int): Option[Int] = history.lift(i) match { - // If i < 0, it means the user is pressing `down` too many times, which - // means it doesn't show anything but we shouldn't show an error - case None if i < 0 => Some(-1) - case None => None - case Some(s) if s.contains(searchTerm) && !s.contentEquals(skipped) => - Some(i) - case _ => rec(i + indexIncrement) - } - - val newHistoryIndex = rec(startIndex) - val foundIndex = newHistoryIndex.find(_ != -1) - val newBuffer = foundIndex match { - case None => searchTerm - case Some(i) => history(i).toVector - } - - val newCursor = foundIndex match { - case None => newBuffer.length - case Some(i) => history(i).indexOfSlice(searchTerm) + searchTerm.length - } - - (newHistoryIndex, newBuffer, newCursor) - } - - val emptySearchMessage = - s" ...enter the string to search for, then `up` for more" - val cannotFindSearchMessage = - s" ...can't be found in history; re-starting search" -} diff --git a/src/dotty/tools/dotc/repl/ammonite/filters/ReadlineFilters.scala b/src/dotty/tools/dotc/repl/ammonite/filters/ReadlineFilters.scala deleted file mode 100644 index eb79f2b04..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/filters/ReadlineFilters.scala +++ /dev/null @@ -1,165 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite -package terminal -package filters - -import terminal.FilterTools._ -import terminal.SpecialKeys._ -import terminal.{DelegateFilter, Filter, Terminal} -/** - * Filters for injection of readline-specific hotkeys, the sort that - * are available in bash, python and most other interactive command-lines - */ -object ReadlineFilters { - // www.bigsmoke.us/readline/shortcuts - // Ctrl-b <- one char - // Ctrl-f -> one char - // Alt-b <- one word - // Alt-f -> one word - // Ctrl-a <- start of line - // Ctrl-e -> end of line - // Ctrl-x-x Toggle start/end - - // Backspace <- delete char - // Del -> delete char - // Ctrl-u <- delete all - // Ctrl-k -> delete all - // Alt-d -> delete word - // Ctrl-w <- delete word - - // Ctrl-u/- Undo - // Ctrl-l clear screen - - // Ctrl-k -> cut all - // Alt-d -> cut word - // Alt-Backspace <- cut word - // Ctrl-y paste last cut - - /** - * Basic readline-style navigation, using all the obscure alphabet hotkeys - * rather than using arrows - */ - lazy val navFilter = Filter.merge( - Case(Ctrl('b'))((b, c, m) => (b, c - 1)), // <- one char - Case(Ctrl('f'))((b, c, m) => (b, c + 1)), // -> one char - Case(Alt + "b")((b, c, m) => GUILikeFilters.wordLeft(b, c)), // <- one word - Case(Alt + "B")((b, c, m) => GUILikeFilters.wordLeft(b, c)), // <- one word - Case(LinuxCtrlLeft)((b, c, m) => GUILikeFilters.wordLeft(b, c)), // <- one word - Case(Alt + "f")((b, c, m) => GUILikeFilters.wordRight(b, c)), // -> one word - Case(Alt + "F")((b, c, m) => GUILikeFilters.wordRight(b, c)), // -> one word - Case(LinuxCtrlRight)((b, c, m) => GUILikeFilters.wordRight(b, c)), // -> one word - Case(Home)((b, c, m) => BasicFilters.moveStart(b, c, m.width)), // <- one line - Case(HomeScreen)((b, c, m) => BasicFilters.moveStart(b, c, m.width)), // <- one line - Case(Ctrl('a'))((b, c, m) => BasicFilters.moveStart(b, c, m.width)), - Case(End)((b, c, m) => BasicFilters.moveEnd(b, c, m.width)), // -> one line - Case(EndScreen)((b, c, m) => BasicFilters.moveEnd(b, c, m.width)), // -> one line - Case(Ctrl('e'))((b, c, m) => BasicFilters.moveEnd(b, c, m.width)), - Case(Alt + "t")((b, c, m) => transposeWord(b, c)), - Case(Alt + "T")((b, c, m) => transposeWord(b, c)), - Case(Ctrl('t'))((b, c, m) => transposeLetter(b, c)) - ) - - def transposeLetter(b: Vector[Char], c: Int) = - // If there's no letter before the cursor to transpose, don't do anything - if (c == 0) (b, c) - else if (c == b.length) (b.dropRight(2) ++ b.takeRight(2).reverse, c) - else (b.patch(c-1, b.slice(c-1, c+1).reverse, 2), c + 1) - - def transposeWord(b: Vector[Char], c: Int) = { - val leftStart0 = GUILikeFilters.consumeWord(b, c - 1, -1, 1) - val leftEnd0 = GUILikeFilters.consumeWord(b, leftStart0, 1, 0) - val rightEnd = GUILikeFilters.consumeWord(b, c, 1, 0) - val rightStart = GUILikeFilters.consumeWord(b, rightEnd - 1, -1, 1) - - // If no word to the left to transpose, do nothing - if (leftStart0 == 0 && rightStart == 0) (b, c) - else { - val (leftStart, leftEnd) = - // If there is no word to the *right* to transpose, - // transpose the two words to the left instead - if (leftEnd0 == b.length && rightEnd == b.length) { - val leftStart = GUILikeFilters.consumeWord(b, leftStart0 - 1, -1, 1) - val leftEnd = GUILikeFilters.consumeWord(b, leftStart, 1, 0) - (leftStart, leftEnd) - }else (leftStart0, leftEnd0) - - val newB = - b.slice(0, leftStart) ++ - b.slice(rightStart, rightEnd) ++ - b.slice(leftEnd, rightStart) ++ - b.slice(leftStart, leftEnd) ++ - b.slice(rightEnd, b.length) - - (newB, rightEnd) - } - } - - /** - * All the cut-pasting logic, though for many people they simply - * use these shortcuts for deleting and don't use paste much at all. - */ - case class CutPasteFilter() extends DelegateFilter { - def identifier = "CutPasteFilter" - var accumulating = false - var currentCut = Vector.empty[Char] - def prepend(b: Vector[Char]) = { - if (accumulating) currentCut = b ++ currentCut - else currentCut = b - accumulating = true - } - def append(b: Vector[Char]) = { - if (accumulating) currentCut = currentCut ++ b - else currentCut = b - accumulating = true - } - def cutCharLeft(b: Vector[Char], c: Int) = { - /* Do not edit current cut. Zsh(zle) & Bash(readline) do not edit the yank ring for Ctrl-h */ - (b patch(from = c - 1, patch = Nil, replaced = 1), c - 1) - } - - def cutAllLeft(b: Vector[Char], c: Int) = { - prepend(b.take(c)) - (b.drop(c), 0) - } - def cutAllRight(b: Vector[Char], c: Int) = { - append(b.drop(c)) - (b.take(c), c) - } - - def cutWordRight(b: Vector[Char], c: Int) = { - val start = GUILikeFilters.consumeWord(b, c, 1, 0) - append(b.slice(c, start)) - (b.take(c) ++ b.drop(start), c) - } - - def cutWordLeft(b: Vector[Char], c: Int) = { - val start = GUILikeFilters.consumeWord(b, c - 1, -1, 1) - prepend(b.slice(start, c)) - (b.take(start) ++ b.drop(c), start) - } - - def paste(b: Vector[Char], c: Int) = { - accumulating = false - (b.take(c) ++ currentCut ++ b.drop(c), c + currentCut.length) - } - - def filter = Filter.merge( - Case(Ctrl('u'))((b, c, m) => cutAllLeft(b, c)), - Case(Ctrl('k'))((b, c, m) => cutAllRight(b, c)), - Case(Alt + "d")((b, c, m) => cutWordRight(b, c)), - Case(Ctrl('w'))((b, c, m) => cutWordLeft(b, c)), - Case(Alt + "\u007f")((b, c, m) => cutWordLeft(b, c)), - // weird hacks to make it run code every time without having to be the one - // handling the input; ideally we'd change Filter to be something - // other than a PartialFunction, but for now this will do. - - // If some command goes through that's not appending/prepending to the - // kill ring, stop appending and allow the next kill to override it - Filter.wrap("ReadLineFilterWrap") {_ => accumulating = false; None}, - Case(Ctrl('h'))((b, c, m) => cutCharLeft(b, c)), - Case(Ctrl('y'))((b, c, m) => paste(b, c)) - ) - } -} diff --git a/src/dotty/tools/dotc/repl/ammonite/filters/UndoFilter.scala b/src/dotty/tools/dotc/repl/ammonite/filters/UndoFilter.scala deleted file mode 100644 index c265a7a4c..000000000 --- a/src/dotty/tools/dotc/repl/ammonite/filters/UndoFilter.scala +++ /dev/null @@ -1,157 +0,0 @@ -package dotty.tools -package dotc -package repl -package ammonite -package terminal -package filters - -import terminal.FilterTools._ -import terminal.LazyList.~: -import terminal._ -import scala.collection.mutable - -/** - * A filter that implements "undo" functionality in the ammonite REPL. It - * shares the same `Ctrl -` hotkey that the bash undo, but shares behavior - * with the undo behavior in desktop text editors: - * - * - Multiple `delete`s in a row get collapsed - * - In addition to edits you can undo cursor movements: undo will bring your - * cursor back to location of previous edits before it undoes them - * - Provides "redo" functionality under `Alt -`/`Esc -`: un-undo the things - * you didn't actually want to undo! - * - * @param maxUndo: the maximum number of undo-frames that are stored. - */ -case class UndoFilter(maxUndo: Int = 25) extends DelegateFilter { - def identifier = "UndoFilter" - /** - * The current stack of states that undo/redo would cycle through. - * - * Not really the appropriate data structure, since when it reaches - * `maxUndo` in length we remove one element from the start whenever we - * append one element to the end, which costs `O(n)`. On the other hand, - * It also costs `O(n)` to maintain the buffer of previous states, and - * so `n` is probably going to be pretty small anyway (tens?) so `O(n)` - * is perfectly fine. - */ - val undoBuffer = mutable.Buffer[(Vector[Char], Int)](Vector[Char]() -> 0) - - /** - * The current position in the undoStack that the terminal is currently in. - */ - var undoIndex = 0 - /** - * An enum representing what the user is "currently" doing. Used to - * collapse sequential actions into one undo step: e.g. 10 plai - * chars typed becomes 1 undo step, or 10 chars deleted becomes one undo - * step, but 4 chars typed followed by 3 chars deleted followed by 3 chars - * typed gets grouped into 3 different undo steps - */ - var state = UndoState.Default - def currentUndo = undoBuffer(undoBuffer.length - undoIndex - 1) - - def undo(b: Vector[Char], c: Int) = { - val msg = - if (undoIndex >= undoBuffer.length - 1) UndoFilter.cannotUndoMsg - else { - undoIndex += 1 - state = UndoState.Default - UndoFilter.undoMsg - } - val (b1, c1) = currentUndo - (b1, c1, msg) - } - - def redo(b: Vector[Char], c: Int) = { - val msg = - if (undoIndex <= 0) UndoFilter.cannotRedoMsg - else { - undoIndex -= 1 - state = UndoState.Default - UndoFilter.redoMsg - } - - currentUndo - val (b1, c1) = currentUndo - (b1, c1, msg) - } - - def wrap(bc: (Vector[Char], Int, Ansi.Str), rest: LazyList[Int]) = { - val (b, c, msg) = bc - TS(rest, b, c, msg) - } - - def pushUndos(b: Vector[Char], c: Int) = { - val (lastB, lastC) = currentUndo - // Since we don't have access to the `typingFilter` in this code, we - // instead attempt to reverse-engineer "what happened" to the buffer by - // comparing the old one with the new. - // - // It turns out that it's not that hard to identify the few cases we care - // about, since they're all result in either 0 or 1 chars being different - // between old and new buffers. - val newState = - // Nothing changed means nothing changed - if (lastC == c && lastB == b) state - // if cursor advanced 1, and buffer grew by 1 at the cursor, we're typing - else if (lastC + 1 == c && lastB == b.patch(c-1, Nil, 1)) UndoState.Typing - // cursor moved left 1, and buffer lost 1 char at that point, we're deleting - else if (lastC - 1 == c && lastB.patch(c, Nil, 1) == b) UndoState.Deleting - // cursor didn't move, and buffer lost 1 char at that point, we're also deleting - else if (lastC == c && lastB.patch(c - 1, Nil, 1) == b) UndoState.Deleting - // cursor moved around but buffer didn't change, we're navigating - else if (lastC != c && lastB == b) UndoState.Navigating - // otherwise, sit in the "Default" state where every change is recorded. - else UndoState.Default - - if (state != newState || newState == UndoState.Default && (lastB, lastC) != (b, c)) { - // If something changes: either we enter a new `UndoState`, or we're in - // the `Default` undo state and the terminal buffer/cursor change, then - // truncate the `undoStack` and add a new tuple to the stack that we can - // build upon. This means that we lose all ability to re-do actions after - // someone starts making edits, which is consistent with most other - // editors - state = newState - undoBuffer.remove(undoBuffer.length - undoIndex, undoIndex) - undoIndex = 0 - - if (undoBuffer.length == maxUndo) undoBuffer.remove(0) - - undoBuffer.append(b -> c) - } else if (undoIndex == 0 && (b, c) != undoBuffer(undoBuffer.length - 1)) { - undoBuffer(undoBuffer.length - 1) = (b, c) - } - - state = newState - } - - def filter = Filter.merge( - Filter.wrap("undoFilterWrapped") { - case TS(q ~: rest, b, c, _) => - pushUndos(b, c) - None - }, - Filter("undoFilter") { - case TS(31 ~: rest, b, c, _) => wrap(undo(b, c), rest) - case TS(27 ~: 114 ~: rest, b, c, _) => wrap(undo(b, c), rest) - case TS(27 ~: 45 ~: rest, b, c, _) => wrap(redo(b, c), rest) - } - ) -} - - -sealed class UndoState(override val toString: String) -object UndoState { - val Default = new UndoState("Default") - val Typing = new UndoState("Typing") - val Deleting = new UndoState("Deleting") - val Navigating = new UndoState("Navigating") -} - -object UndoFilter { - val undoMsg = Ansi.Color.Blue(" ...undoing last action, `Alt -` or `Esc -` to redo") - val cannotUndoMsg = Ansi.Color.Blue(" ...no more actions to undo") - val redoMsg = Ansi.Color.Blue(" ...redoing last action") - val cannotRedoMsg = Ansi.Color.Blue(" ...no more actions to redo") -} diff --git a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala deleted file mode 100644 index 95f468995..000000000 --- a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ /dev/null @@ -1,63 +0,0 @@ -package dotty.tools -package dotc -package reporting - -import core.Contexts._ -import java.io.{ BufferedReader, PrintWriter } -import diagnostic.{ Message, MessageContainer } -import diagnostic.messages.{ Error, Warning, ConditionalWarning } - -/** - * This class implements a Reporter that displays messages on a text console - */ -class ConsoleReporter( - reader: BufferedReader = Console.in, - writer: PrintWriter = new PrintWriter(Console.err, true) -) extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with MessageRendering { - - import MessageContainer._ - - /** maximal number of error messages to be printed */ - protected def ErrorLimit = 100 - - /** Prints the message. */ - def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() } - - /** Prints the message with the given position indication. */ - def doReport(m: MessageContainer)(implicit ctx: Context): Unit = { - val didPrint = m match { - case m: Error => - printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m))) - if (ctx.settings.prompt.value) displayPrompt() - true - case m: ConditionalWarning if !m.enablingOption.value => - false - case m => - printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m))) - true - } - - if (didPrint && ctx.shouldExplain(m)) - printMessage(explanation(m.contained)) - else if (didPrint && m.contained.explanation.nonEmpty) - printMessage("\nlonger explanation available when compiling with `-explain`") - } - - /** Show prompt if `-Xprompt` is passed as a flag to the compiler */ - def displayPrompt()(implicit ctx: Context): Unit = { - printMessage("\na)bort, s)tack, r)esume: ") - flush() - if (reader != null) { - val response = reader.read().asInstanceOf[Char].toLower - if (response == 'a' || response == 's') { - Thread.dumpStack() - if (response == 'a') - sys.exit(1) - } - print("\n") - flush() - } - } - - override def flush()(implicit ctx: Context): Unit = { writer.flush() } -} diff --git a/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala b/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala deleted file mode 100644 index ba1ab9b33..000000000 --- a/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala +++ /dev/null @@ -1,21 +0,0 @@ -package dotty.tools -package dotc -package reporting - -import core.Contexts.Context -import diagnostic.MessageContainer - -/** - * This trait implements `isHidden` so that we avoid reporting non-sensical messages. - */ -trait HideNonSensicalMessages extends Reporter { - /** Hides non-sensical messages, unless we haven't reported any error yet or - * `-Yshow-suppressed-errors` is set. - */ - override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = - super.isHidden(m) || { - m.isNonSensical && - hasErrors && // if there are no errors yet, report even if diagnostic is non-sensical - !ctx.settings.YshowSuppressedErrors.value - } -} diff --git a/src/dotty/tools/dotc/reporting/MessageRendering.scala b/src/dotty/tools/dotc/reporting/MessageRendering.scala deleted file mode 100644 index 24d583b19..000000000 --- a/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ /dev/null @@ -1,145 +0,0 @@ -package dotty.tools -package dotc -package reporting - -import core.Contexts.Context -import core.Decorators._ -import printing.Highlighting.{Blue, Red} -import diagnostic.{Message, MessageContainer, NoExplanation} -import diagnostic.messages._ -import util.SourcePosition - -import scala.collection.mutable - -trait MessageRendering { - /** Remove ANSI coloring from `str`, useful for getting real length of - * strings - * - * @return string stripped of ANSI escape codes - */ - def stripColor(str: String): String = - str.replaceAll("\u001B\\[[;\\d]*m", "") - - /** When inlining a method call, if there's an error we'd like to get the - * outer context and the `pos` at which the call was inlined. - * - * @return a list of strings with inline locations - */ - def outer(pos: SourcePosition, prefix: String)(implicit ctx: Context): List[String] = - if (pos.outer.exists) { - s"$prefix| This location is in code that was inlined at ${pos.outer}" :: - outer(pos.outer, prefix) - } else Nil - - /** Get the sourcelines before and after the position, as well as the offset - * for rendering line numbers - * - * @return (lines before error, lines after error, line numbers offset) - */ - def sourceLines(pos: SourcePosition)(implicit ctx: Context): (List[String], List[String], Int) = { - var maxLen = Int.MinValue - def render(xs: List[Int]) = - xs.map(pos.source.offsetToLine(_)) - .map { lineNbr => - val prefix = s"${lineNbr + 1} |" - maxLen = math.max(maxLen, prefix.length) - (prefix, pos.lineContent(lineNbr).stripLineEnd) - } - .map { case (prefix, line) => - val lnum = Red(" " * math.max(0, maxLen - prefix.length) + prefix) - hl"$lnum$line" - } - - val (before, after) = pos.beforeAndAfterPoint - (render(before), render(after), maxLen) - } - - /** The column markers aligned under the error */ - def columnMarker(pos: SourcePosition, offset: Int)(implicit ctx: Context): String = { - val prefix = " " * (offset - 1) - val whitespace = " " * pos.startColumn - val carets = Red { - if (pos.startLine == pos.endLine) - "^" * math.max(1, pos.endColumn - pos.startColumn) - else "^" - } - - s"$prefix|$whitespace${carets.show}" - } - - /** The error message (`msg`) aligned under `pos` - * - * @return aligned error message - */ - def errorMsg(pos: SourcePosition, msg: String, offset: Int)(implicit ctx: Context): String = { - val leastWhitespace = msg.lines.foldLeft(Int.MaxValue) { (minPad, line) => - val lineLength = stripColor(line).length - val currPad = math.min( - math.max(0, ctx.settings.pageWidth.value - offset - lineLength), - offset + pos.startColumn - ) - - math.min(currPad, minPad) - } - - msg.lines - .map { line => " " * (offset - 1) + "|" + (" " * (leastWhitespace - offset)) + line} - .mkString(sys.props("line.separator")) - } - - /** The separator between errors containing the source file and error type - * - * @return separator containing error location and kind - */ - def posStr(pos: SourcePosition, diagnosticLevel: String, message: Message)(implicit ctx: Context): String = - if (pos.exists) Blue({ - val file = pos.source.file.toString - val errId = - if (message.errorId != NoExplanation.ID) - s"[E${"0" * (3 - message.errorId.toString.length) + message.errorId}] " - else "" - val kind = - if (message.kind == "") diagnosticLevel - else s"${message.kind} $diagnosticLevel" - val prefix = s"-- ${errId}${kind}: $file " - - prefix + - ("-" * math.max(ctx.settings.pageWidth.value - stripColor(prefix).length, 0)) - }).show else "" - - /** Explanation rendered under "Explanation" header */ - def explanation(m: Message)(implicit ctx: Context): String = { - val sb = new StringBuilder( - hl"""| - |${Blue("Explanation")} - |${Blue("===========")}""" - ) - sb.append('\n').append(m.explanation) - if (m.explanation.lastOption != Some('\n')) sb.append('\n') - sb.toString - } - - /** The whole message rendered from `msg` */ - def messageAndPos(msg: Message, pos: SourcePosition, diagnosticLevel: String)(implicit ctx: Context): String = { - val sb = mutable.StringBuilder.newBuilder - sb.append(posStr(pos, diagnosticLevel, msg)).append('\n') - if (pos.exists) { - val (srcBefore, srcAfter, offset) = sourceLines(pos) - val marker = columnMarker(pos, offset) - val err = errorMsg(pos, msg.msg, offset) - sb.append((srcBefore ::: marker :: err :: outer(pos, " " * (offset - 1)) ::: srcAfter).mkString("\n")) - } else sb.append(msg.msg) - sb.toString - } - - def diagnosticLevel(cont: MessageContainer): String = - cont match { - case m: Error => "Error" - case m: FeatureWarning => "Feature Warning" - case m: DeprecationWarning => "Deprecation Warning" - case m: UncheckedWarning => "Unchecked Warning" - case m: MigrationWarning => "Migration Warning" - case m: Warning => "Warning" - case m: Info => "Info" - } -} diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala deleted file mode 100644 index 8477cfe28..000000000 --- a/src/dotty/tools/dotc/reporting/Reporter.scala +++ /dev/null @@ -1,296 +0,0 @@ -package dotty.tools -package dotc -package reporting - -import core.Contexts._ -import util.{SourcePosition, NoSourcePosition} -import core.Decorators.PhaseListDecorator -import collection.mutable -import config.Printers -import java.lang.System.currentTimeMillis -import core.Mode -import dotty.tools.dotc.core.Symbols.Symbol -import diagnostic.messages._ -import diagnostic._ -import Message._ - -object Reporter { - /** Convert a SimpleReporter into a real Reporter */ - def fromSimpleReporter(simple: interfaces.SimpleReporter): Reporter = - new Reporter with UniqueMessagePositions with HideNonSensicalMessages { - override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match { - case m: ConditionalWarning if !m.enablingOption.value => - case _ => - simple.report(m) - } - } -} - -import Reporter._ - -trait Reporting { this: Context => - - /** For sending messages that are printed only if -verbose is set */ - def inform(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = - if (this.settings.verbose.value) this.echo(msg, pos) - - def echo(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = - reporter.report(new Info(msg, pos)) - - def deprecationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - reporter.report(new DeprecationWarning(msg, pos)) - - def migrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - reporter.report(new MigrationWarning(msg, pos)) - - def uncheckedWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - reporter.report(new UncheckedWarning(msg, pos)) - - def featureWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - reporter.report(new FeatureWarning(msg, pos)) - - def featureWarning(feature: String, featureDescription: String, isScala2Feature: Boolean, - featureUseSite: Symbol, required: Boolean, pos: SourcePosition): Unit = { - val req = if (required) "needs to" else "should" - val prefix = if (isScala2Feature) "scala." else "dotty." - val fqname = prefix + "language." + feature - - val explain = { - if (reporter.isReportedFeatureUseSite(featureUseSite)) "" - else { - reporter.reportNewFeatureUseSite(featureUseSite) - s""" - |This can be achieved by adding the import clause 'import $fqname' - |or by setting the compiler option -language:$feature. - |See the Scala docs for value $fqname for a discussion - |why the feature $req be explicitly enabled.""" - } - } - - val msg = s"$featureDescription $req be enabled\nby making the implicit value $fqname visible.$explain" - if (required) error(msg, pos) - else reporter.report(new FeatureWarning(msg, pos)) - } - - def warning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - reporter.report(new Warning(msg, pos)) - - def strictWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - if (this.settings.strict.value) error(msg, pos) - else reporter.report { - new ExtendMessage(() => msg)(_ + "\n(This would be an error under strict mode)").warning(pos) - } - - def error(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - reporter.report(new Error(msg, pos)) - - def errorOrMigrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - if (ctx.scala2Mode) migrationWarning(msg, pos) else error(msg, pos) - - def restrictionError(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = - reporter.report { - new ExtendMessage(() => msg)(m => s"Implementation restriction: $m").error(pos) - } - - def incompleteInputError(msg: => Message, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit = - reporter.incomplete(new Error(msg, pos))(ctx) - - /** Log msg if settings.log contains the current phase. - * See [[config.CompilerCommand#explainAdvanced]] for the exact meaning of - * "contains" here. - */ - def log(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = - if (this.settings.log.value.containsPhase(phase)) - echo(s"[log ${ctx.phasesStack.reverse.mkString(" -> ")}] $msg", pos) - - def debuglog(msg: => String): Unit = - if (ctx.debug) log(msg) - - def informTime(msg: => String, start: Long): Unit = { - def elapsed = s" in ${currentTimeMillis - start}ms" - informProgress(msg + elapsed) - } - - def informProgress(msg: => String) = - inform("[" + msg + "]") - - def trace[T](msg: => String)(value: T) = { - log(msg + " " + value) - value - } - - def debugwarn(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = - if (this.settings.debug.value) warning(msg, pos) - - @inline - def debugTraceIndented[TD](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => TD): TD = - conditionalTraceIndented(this.settings.debugTrace.value, question, printer, show)(op) - - @inline - def conditionalTraceIndented[TC](cond: Boolean, question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => TC): TC = - if (cond) traceIndented[TC](question, printer, show)(op) - else op - - @inline - def traceIndented[T](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T = - if (printer eq config.Printers.noPrinter) op - else doTraceIndented[T](question, printer, show)(op) - - private def doTraceIndented[T](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T = { - def resStr(res: Any): String = res match { - case res: printing.Showable if show => res.show - case _ => String.valueOf(res) - } - // Avoid evaluating question multiple time, since each evaluation - // may cause some extra logging output. - lazy val q: String = question - doTraceIndented[T](s"==> $q?", (res: Any) => s"<== $q = ${resStr(res)}")(op) - } - - def doTraceIndented[T](leading: => String, trailing: Any => String)(op: => T): T = - if (ctx.mode.is(Mode.Printing)) op - else { - var finalized = false - var logctx = this - while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer - def finalize(result: Any, note: String) = - if (!finalized) { - base.indent -= 1 - logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note") - finalized = true - } - try { - logctx.log(s"${base.indentTab * base.indent}$leading") - base.indent += 1 - val res = op - finalize(res, "") - res - } catch { - case ex: Throwable => - finalize("<missing>", s" (with exception $ex)") - throw ex - } - } - - /** Implements a fold that applies the function `f` to the result of `op` if - * there are no new errors in the reporter - * - * @param op operation checked for errors - * @param f function applied to result of op - * @return either the result of `op` if it had errors or the result of `f` - * applied to it - */ - def withNoError[A, B >: A](op: => A)(f: A => B): B = { - val before = reporter.errorCount - val op0 = op - - if (reporter.errorCount > before) op0 - else f(op0) - } -} - -/** - * This interface provides methods to issue information, warning and - * error messages. - */ -abstract class Reporter extends interfaces.ReporterResult { - - /** Report a diagnostic */ - def doReport(m: MessageContainer)(implicit ctx: Context): Unit - - /** Whether very long lines can be truncated. This exists so important - * debugging information (like printing the classpath) is not rendered - * invisible due to the max message length. - */ - private var _truncationOK: Boolean = true - def truncationOK = _truncationOK - def withoutTruncating[T](body: => T): T = { - val saved = _truncationOK - _truncationOK = false - try body - finally _truncationOK = saved - } - - type ErrorHandler = MessageContainer => Context => Unit - private var incompleteHandler: ErrorHandler = d => c => report(d)(c) - def withIncompleteHandler[T](handler: ErrorHandler)(op: => T): T = { - val saved = incompleteHandler - incompleteHandler = handler - try op - finally incompleteHandler = saved - } - - var errorCount = 0 - var warningCount = 0 - def hasErrors = errorCount > 0 - def hasWarnings = warningCount > 0 - private var errors: List[Error] = Nil - def allErrors = errors - - /** Have errors been reported by this reporter, or in the - * case where this is a StoreReporter, by an outer reporter? - */ - def errorsReported = hasErrors - - private[this] var reportedFeaturesUseSites = Set[Symbol]() - def isReportedFeatureUseSite(featureTrait: Symbol): Boolean = reportedFeaturesUseSites.contains(featureTrait) - def reportNewFeatureUseSite(featureTrait: Symbol): Unit = reportedFeaturesUseSites += featureTrait - - val unreportedWarnings = new mutable.HashMap[String, Int] { - override def default(key: String) = 0 - } - - def report(m: MessageContainer)(implicit ctx: Context): Unit = - if (!isHidden(m)) { - doReport(m)(ctx.addMode(Mode.Printing)) - m match { - case m: ConditionalWarning if !m.enablingOption.value => unreportedWarnings(m.enablingOption.name) += 1 - case m: Warning => warningCount += 1 - case m: Error => - errors = m :: errors - errorCount += 1 - case m: Info => // nothing to do here - // match error if d is something else - } - } - - def incomplete(m: MessageContainer)(implicit ctx: Context): Unit = - incompleteHandler(m)(ctx) - - /** Summary of warnings and errors */ - def summary: String = { - val b = new mutable.ListBuffer[String] - if (warningCount > 0) - b += countString(warningCount, "warning") + " found" - if (errorCount > 0) - b += countString(errorCount, "error") + " found" - for ((settingName, count) <- unreportedWarnings) - b += s"there were $count ${settingName.tail} warning(s); re-run with $settingName for details" - b.mkString("\n") - } - - /** Print the summary of warnings and errors */ - def printSummary(implicit ctx: Context): Unit = { - val s = summary - if (s != "") ctx.echo(s) - } - - /** Returns a string meaning "n elements". */ - protected def countString(n: Int, elements: String): String = n match { - case 0 => "no " + elements + "s" - case 1 => "one " + elements - case 2 => "two " + elements + "s" - case 3 => "three " + elements + "s" - case 4 => "four " + elements + "s" - case _ => n + " " + elements + "s" - } - - /** Should this diagnostic not be reported at all? */ - def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing) - - /** Does this reporter contain not yet reported errors or warnings? */ - def hasPending: Boolean = false - - /** Issue all error messages in this reporter to next outer one, or make sure they are written. */ - def flush()(implicit ctx: Context): Unit = {} -} diff --git a/src/dotty/tools/dotc/reporting/StoreReporter.scala b/src/dotty/tools/dotc/reporting/StoreReporter.scala deleted file mode 100644 index 586273c2e..000000000 --- a/src/dotty/tools/dotc/reporting/StoreReporter.scala +++ /dev/null @@ -1,46 +0,0 @@ -package dotty.tools -package dotc -package reporting - -import core.Contexts.Context -import collection.mutable -import config.Printers.typr -import diagnostic.MessageContainer -import diagnostic.messages._ - -/** This class implements a Reporter that stores all messages - * - * Beware that this reporter can leak memory, and force messages in two - * scenarios: - * - * - During debugging `config.Printers.typr` is set from `noPrinter` to `new - * Printer`, which forces the message - * - The reporter is not flushed and the message containers capture a - * `Context` (about 4MB) - */ -class StoreReporter(outer: Reporter) extends Reporter { - - private var infos: mutable.ListBuffer[MessageContainer] = null - - def doReport(m: MessageContainer)(implicit ctx: Context): Unit = { - typr.println(s">>>> StoredError: ${m.message}") // !!! DEBUG - if (infos == null) infos = new mutable.ListBuffer - infos += m - } - - override def hasPending: Boolean = infos != null && { - infos exists { - case _: Error => true - case _: Warning => true - case _ => false - } - } - - override def flush()(implicit ctx: Context) = - if (infos != null) { - infos.foreach(ctx.reporter.report(_)) - infos = null - } - - override def errorsReported = hasErrors || outer.errorsReported -} diff --git a/src/dotty/tools/dotc/reporting/ThrowingReporter.scala b/src/dotty/tools/dotc/reporting/ThrowingReporter.scala deleted file mode 100644 index d8e03ab66..000000000 --- a/src/dotty/tools/dotc/reporting/ThrowingReporter.scala +++ /dev/null @@ -1,20 +0,0 @@ -package dotty.tools -package dotc -package reporting - -import core.Contexts.Context -import collection.mutable -import diagnostic.MessageContainer -import diagnostic.messages.Error -import Reporter._ - -/** - * This class implements a Reporter that throws all errors and sends warnings and other - * info to the underlying reporter. - */ -class ThrowingReporter(reportInfo: Reporter) extends Reporter { - def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match { - case _: Error => throw m - case _ => reportInfo.doReport(m) - } -} diff --git a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala deleted file mode 100644 index 6fd971c2a..000000000 --- a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala +++ /dev/null @@ -1,32 +0,0 @@ -package dotty.tools -package dotc -package reporting - -import scala.collection.mutable -import util.{SourcePosition, SourceFile} -import core.Contexts.Context -import diagnostic.MessageContainer - -/** This trait implements `isHidden` so that multiple messages per position - * are suppressed, unless they are of increasing severity. */ -trait UniqueMessagePositions extends Reporter { - - private val positions = new mutable.HashMap[(SourceFile, Int), Int] - - /** Logs a position and returns true if it was already logged. - * @note Two positions are considered identical for logging if they have the same point. - */ - override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = - super.isHidden(m) || { - m.pos.exists && { - var shouldHide = false - for (pos <- m.pos.start to m.pos.end) { - positions get (ctx.source, pos) match { - case Some(level) if level >= m.level => shouldHide = true - case _ => positions((ctx.source, pos)) = m.level - } - } - shouldHide - } - } -} diff --git a/src/dotty/tools/dotc/reporting/diagnostic/Message.scala b/src/dotty/tools/dotc/reporting/diagnostic/Message.scala deleted file mode 100644 index 2497fb216..000000000 --- a/src/dotty/tools/dotc/reporting/diagnostic/Message.scala +++ /dev/null @@ -1,133 +0,0 @@ -package dotty.tools -package dotc -package reporting -package diagnostic - -import util.SourcePosition -import core.Contexts.Context - -import messages._ - -object Message { - /** This implicit conversion provides a fallback for error messages that have - * not yet been ported to the new scheme. Comment out this `implicit def` to - * see where old errors still exist - */ - implicit def toNoExplanation(str: String): Message = - new NoExplanation(str) -} - -/** A `Message` contains all semantic information necessary to easily - * comprehend what caused the message to be logged. Each message can be turned - * into a `MessageContainer` which contains the log level and can later be - * consumed by a subclass of `Reporter`. However, the error position is only - * part of `MessageContainer`, not `Message`. - * - * NOTE: you should not be persisting messages. Most messages take an implicit - * `Context` and these contexts weigh in at about 4mb per instance, as such - * persisting these will result in a memory leak. - * - * Instead use the `persist` method to create an instance that does not keep a - * reference to these contexts. - * - * @param errorId a unique number identifying the message, this will later be - * used to reference documentation online - */ -abstract class Message(val errorId: Int) { self => - import messages._ - - /** The `msg` contains the diagnostic message e.g: - * - * > expected: String - * > found: Int - * - * This message will be placed underneath the position given by the enclosing - * `MessageContainer` - */ - def msg: String - - /** The kind of the error message is something like "Syntax" or "Type - * Mismatch" - */ - def kind: String - - /** The explanation should provide a detailed description of why the error - * occurred and use examples from the user's own code to illustrate how to - * avoid these errors. - */ - def explanation: String - - /** The implicit `Context` in messages is a large thing that we don't want - * persisted. This method gets around that by duplicating the message - * without the implicit context being passed along. - */ - def persist: Message = new Message (errorId) { - val msg = self.msg - val kind = self.kind - val explanation = self.explanation - } -} - -/** An extended message keeps the contained message from being evaluated, while - * allowing for extension for the `msg` string - * - * This is useful when we need to add additional information to an existing - * message. - */ -class ExtendMessage(_msg: () => Message)(f: String => String) { self => - lazy val msg = f(_msg().msg) - lazy val kind = _msg().kind - lazy val explanation = _msg().explanation - lazy val errorId = _msg().errorId - - private def toMessage = new Message(errorId) { - val msg = self.msg - val kind = self.kind - val explanation = self.explanation - } - - /** Enclose this message in an `Error` container */ - def error(pos: SourcePosition) = - new Error(toMessage, pos) - - /** Enclose this message in an `Warning` container */ - def warning(pos: SourcePosition) = - new Warning(toMessage, pos) - - /** Enclose this message in an `Info` container */ - def info(pos: SourcePosition) = - new Info(toMessage, pos) - - /** Enclose this message in an `FeatureWarning` container */ - def featureWarning(pos: SourcePosition) = - new FeatureWarning(toMessage, pos) - - /** Enclose this message in an `UncheckedWarning` container */ - def uncheckedWarning(pos: SourcePosition) = - new UncheckedWarning(toMessage, pos) - - /** Enclose this message in an `DeprecationWarning` container */ - def deprecationWarning(pos: SourcePosition) = - new DeprecationWarning(toMessage, pos) - - /** Enclose this message in an `MigrationWarning` container */ - def migrationWarning(pos: SourcePosition) = - new MigrationWarning(toMessage, pos) -} - -/** The fallback `Message` containing no explanation and having no `kind` */ -class NoExplanation(val msg: String) extends Message(NoExplanation.ID) { - val explanation = "" - val kind = "" -} - -/** The extractor for `NoExplanation` can be used to check whether any error - * lacks an explanation - */ -object NoExplanation { - final val ID = -1 - - def unapply(m: Message): Option[Message] = - if (m.explanation == "") Some(m) - else None -} diff --git a/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala b/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala deleted file mode 100644 index 7fd50bfdc..000000000 --- a/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala +++ /dev/null @@ -1,74 +0,0 @@ -package dotty.tools -package dotc -package reporting -package diagnostic - -import util.SourcePosition -import core.Contexts.Context - -import java.util.Optional - -object MessageContainer { - val nonSensicalStartTag = "<nonsensical>" - val nonSensicalEndTag = "</nonsensical>" - - implicit class MessageContext(val c: Context) extends AnyVal { - def shouldExplain(cont: MessageContainer): Boolean = { - implicit val ctx: Context = c - cont.contained.explanation match { - case "" => false - case _ => ctx.settings.explain.value - } - } - } -} - -class MessageContainer( - msgFn: => Message, - val pos: SourcePosition, - val level: Int -) extends Exception with interfaces.Diagnostic { - import MessageContainer._ - private var myMsg: String = null - private var myIsNonSensical: Boolean = false - private var myContained: Message = null - - override def position: Optional[interfaces.SourcePosition] = - if (pos.exists && pos.source.exists) Optional.of(pos) else Optional.empty() - - /** The message to report */ - def message: String = { - if (myMsg == null) { - myMsg = contained.msg.replaceAll("\u001B\\[[;\\d]*m", "") - if (myMsg.contains(nonSensicalStartTag)) { - myIsNonSensical = true - // myMsg might be composed of several d"..." invocations -> nested - // nonsensical tags possible - myMsg = - myMsg - .replaceAllLiterally(nonSensicalStartTag, "") - .replaceAllLiterally(nonSensicalEndTag, "") - } - } - myMsg - } - - def contained: Message = { - if (myContained == null) - myContained = msgFn - - myContained - } - - /** A message is non-sensical if it contains references to <nonsensical> - * tags. Such tags are inserted by the error diagnostic framework if a - * message contains references to internally generated error types. Normally - * we want to suppress error messages referring to types like this because - * they look weird and are normally follow-up errors to something that was - * diagnosed before. - */ - def isNonSensical = { message; myIsNonSensical } - - override def toString = s"$getClass at $pos: ${message}" - override def getMessage() = message -} diff --git a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala deleted file mode 100644 index 489165e56..000000000 --- a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ /dev/null @@ -1,902 +0,0 @@ -package dotty.tools -package dotc -package reporting -package diagnostic - -import dotc.core._ -import Contexts.Context, Decorators._, Symbols._, Names._, NameOps._, Types._ -import util.{SourceFile, NoSource} -import util.{SourcePosition, NoSourcePosition} -import config.Settings.Setting -import interfaces.Diagnostic.{ERROR, WARNING, INFO} -import printing.Highlighting._ -import printing.Formatting - -object messages { - - // `MessageContainer`s to be consumed by `Reporter` ---------------------- // - class Error( - msgFn: => Message, - pos: SourcePosition - ) extends MessageContainer(msgFn, pos, ERROR) - - class Warning( - msgFn: => Message, - pos: SourcePosition - ) extends MessageContainer(msgFn, pos, WARNING) - - class Info( - msgFn: => Message, - pos: SourcePosition - ) extends MessageContainer(msgFn, pos, INFO) - - abstract class ConditionalWarning( - msgFn: => Message, - pos: SourcePosition - ) extends Warning(msgFn, pos) { - def enablingOption(implicit ctx: Context): Setting[Boolean] - } - - class FeatureWarning( - msgFn: => Message, - pos: SourcePosition - ) extends ConditionalWarning(msgFn, pos) { - def enablingOption(implicit ctx: Context) = ctx.settings.feature - } - - class UncheckedWarning( - msgFn: => Message, - pos: SourcePosition - ) extends ConditionalWarning(msgFn, pos) { - def enablingOption(implicit ctx: Context) = ctx.settings.unchecked - } - - class DeprecationWarning( - msgFn: => Message, - pos: SourcePosition - ) extends ConditionalWarning(msgFn, pos) { - def enablingOption(implicit ctx: Context) = ctx.settings.deprecation - } - - class MigrationWarning( - msgFn: => Message, - pos: SourcePosition - ) extends ConditionalWarning(msgFn, pos) { - def enablingOption(implicit ctx: Context) = ctx.settings.migration - } - - /** Messages - * ======== - * The role of messages is to provide the necessary details for a simple to - * understand diagnostic event. Each message can be turned into a message - * container (one of the above) by calling the appropriate method on them. - * For instance: - * - * ```scala - * EmptyCatchBlock(tree).error(pos) // res: Error - * EmptyCatchBlock(tree).warning(pos) // res: Warning - * ``` - */ - import ast.Trees._ - import ast.untpd - import ast.tpd - - /** Helper methods for messages */ - def implicitClassRestrictionsText(implicit ctx: Context) = - hl"""|${NoColor("For a full list of restrictions on implicit classes visit")} - |${Blue("http://docs.scala-lang.org/overviews/core/implicit-classes.html")}""" - - - // Syntax Errors ---------------------------------------------------------- // - abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: Int)(implicit ctx: Context) - extends Message(errNo) { - val explanation = { - val tryString = tryBody match { - case Block(Nil, untpd.EmptyTree) => "{}" - case _ => tryBody.show - } - - val code1 = - s"""|import scala.util.control.NonFatal - | - |try $tryString catch { - | case NonFatal(e) => ??? - |}""".stripMargin - - val code2 = - s"""|try $tryString finally { - | // perform your cleanup here! - |}""".stripMargin - - hl"""|A ${"try"} expression should be followed by some mechanism to handle any exceptions - |thrown. Typically a ${"catch"} expression follows the ${"try"} and pattern matches - |on any expected exceptions. For example: - | - |$code1 - | - |It is also possible to follow a ${"try"} immediately by a ${"finally"} - letting the - |exception propagate - but still allowing for some clean up in ${"finally"}: - | - |$code2 - | - |It is recommended to use the ${"NonFatal"} extractor to catch all exceptions as it - |correctly handles transfer functions like ${"return"}.""" - } - } - - case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context) - extends EmptyCatchOrFinallyBlock(tryBody, 1) { - val kind = "Syntax" - val msg = - hl"""|The ${"catch"} block does not contain a valid expression, try - |adding a case like - `${"case e: Exception =>"}` to the block""" - } - - case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context) - extends EmptyCatchOrFinallyBlock(tryBody, 2) { - val kind = "Syntax" - val msg = - hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting - |its body in a block; no exceptions are handled.""" - } - - case class DeprecatedWithOperator()(implicit ctx: Context) - extends Message(3) { - val kind = "Syntax" - val msg = - hl"""${"with"} as a type operator has been deprecated; use `&' instead""" - val explanation = - hl"""|Dotty introduces intersection types - `&' types. These replace the - |use of the ${"with"} keyword. There are a few differences in - |semantics between intersection types and using `${"with"}'.""" - } - - case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context) - extends Message(4) { - val kind = "Syntax" - val msg = - hl"""|A ${"case class"} must have at least one parameter list""" - - val explanation = - hl"""|${cdef.name} must have at least one parameter list, if you would rather - |have a singleton representation of ${cdef.name}, use a "${"case object"}". - |Or, add an explicit `()' as a parameter list to ${cdef.name}.""" - } - - - // Type Errors ------------------------------------------------------------ // - case class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(implicit ctx: Context) - extends Message(5) { - val kind = "Naming" - val msg = em"duplicate pattern variable: `${bind.name}`" - - val explanation = { - val pat = tree.pat.show - val guard = tree.guard match { - case untpd.EmptyTree => "" - case guard => s"if ${guard.show}" - } - - val body = tree.body match { - case Block(Nil, untpd.EmptyTree) => "" - case body => s" ${body.show}" - } - - val caseDef = s"case $pat$guard => $body" - - hl"""|For each ${"case"} bound variable names have to be unique. In: - | - |$caseDef - | - |`${bind.name}` is not unique. Rename one of the bound variables!""" - } - } - - case class MissingIdent(tree: untpd.Ident, treeKind: String, name: String)(implicit ctx: Context) - extends Message(6) { - val kind = "Unbound Identifier" - val msg = em"not found: $treeKind$name" - - val explanation = { - hl"""|The identifier for `$treeKind$name` is not bound, that is, - |no declaration for this identifier can be found. - |That can happen for instance if $name or its declaration has either been - |misspelt, or if you're forgetting an import""" - } - } - - case class TypeMismatch(found: Type, expected: Type, whyNoMatch: String = "", implicitFailure: String = "")(implicit ctx: Context) - extends Message(7) { - val kind = "Type Mismatch" - val msg = { - val (where, printCtx) = Formatting.disambiguateTypes(found, expected) - val (fnd, exp) = Formatting.typeDiff(found, expected)(printCtx) - s"""|found: $fnd - |required: $exp - | - |$where""".stripMargin + whyNoMatch + implicitFailure - } - - val explanation = "" - } - - case class NotAMember(site: Type, name: Name, selected: String)(implicit ctx: Context) - extends Message(8) { - val kind = "Member Not Found" - - val msg = { - import core.Flags._ - val maxDist = 3 - val decls = site.decls.flatMap { sym => - if (sym.is(Synthetic | PrivateOrLocal) || sym.isConstructor) Nil - else List((sym.name.show, sym)) - } - - // Calculate Levenshtein distance - def distance(n1: Iterable[_], n2: Iterable[_]) = - n1.foldLeft(List.range(0, n2.size)) { (prev, x) => - (prev zip prev.tail zip n2).scanLeft(prev.head + 1) { - case (h, ((d, v), y)) => math.min( - math.min(h + 1, v + 1), - if (x == y) d else d + 1 - ) - } - }.last - - // Count number of wrong characters - def incorrectChars(x: (String, Int, Symbol)): (String, Symbol, Int) = { - val (currName, _, sym) = x - val matching = name.show.zip(currName).foldLeft(0) { - case (acc, (x,y)) => if (x != y) acc + 1 else acc - } - (currName, sym, matching) - } - - // Get closest match in `site` - val closest = - decls - .map { case (n, sym) => (n, distance(n, name.show), sym) } - .collect { case (n, dist, sym) if dist <= maxDist => (n, dist, sym) } - .groupBy(_._2).toList - .sortBy(_._1) - .headOption.map(_._2).getOrElse(Nil) - .map(incorrectChars).toList - .sortBy(_._3) - .take(1).map { case (n, sym, _) => (n, sym) } - - val siteName = site match { - case site: NamedType => site.name.show - case site => i"$site" - } - - val closeMember = closest match { - case (n, sym) :: Nil => hl""" - did you mean `${s"$siteName.$n"}`?""" - case Nil => "" - case _ => assert( - false, - "Could not single out one distinct member to match on input with" - ) - } - - ex"$selected `$name` is not a member of $site$closeMember" - } - - val explanation = "" - } - - case class EarlyDefinitionsNotSupported()(implicit ctx: Context) - extends Message(9) { - val kind = "Syntax" - val msg = "early definitions are not supported; use trait parameters instead" - - val explanation = { - val code1 = - """|trait Logging { - | val f: File - | f.open() - | onExit(f.close()) - | def log(msg: String) = f.write(msg) - |} - | - |class B extends Logging { - | val f = new File("log.data") // triggers a NullPointerException - |} - | - |// early definition gets around the NullPointerException - |class C extends { - | val f = new File("log.data") - |} with Logging""".stripMargin - - val code2 = - """|trait Logging(f: File) { - | f.open() - | onExit(f.close()) - | def log(msg: String) = f.write(msg) - |} - | - |class C extends Logging(new File("log.data"))""".stripMargin - - hl"""|Earlier versions of Scala did not support trait parameters and "early - |definitions" (also known as "early initializers") were used as an alternative. - | - |Example of old syntax: - | - |$code1 - | - |The above code can now be written as: - | - |$code2 - |""" - } - } - - case class TopLevelImplicitClass(cdef: untpd.TypeDef)(implicit ctx: Context) - extends Message(10) { - val kind = "Syntax" - val msg = hl"""An ${"implicit class"} may not be top-level""" - - val explanation = { - val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef - val exampleArgs = - constr0.vparamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ") - def defHasBody[T] = impl.body.exists(!_.isEmpty) - val exampleBody = if (defHasBody) "{\n ...\n }" else "" - hl"""|There may not be any method, member or object in scope with the same name as - |the implicit class and a case class automatically gets a companion object with - |the same name created by the compiler which would cause a naming conflict if it - |were allowed. - | - |""" + implicitClassRestrictionsText + hl"""| - | - |To resolve the conflict declare ${cdef.name} inside of an ${"object"} then import the class - |from the object at the use site if needed, for example: - | - |object Implicits { - | implicit class ${cdef.name}($exampleArgs)$exampleBody - |} - | - |// At the use site: - |import Implicits.${cdef.name}""" - } - } - - case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context) - extends Message(11) { - val kind = "Syntax" - val msg = hl"""A ${"case class"} may not be defined as ${"implicit"}""" - - val explanation = - hl"""|implicit classes may not be case classes. Instead use a plain class: - | - |implicit class ${cdef.name}... - | - |""" + implicitClassRestrictionsText - } - - case class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(implicit ctx: Context) - extends Message(12) { - val kind = "Syntax" - val msg = hl"""${"object"}s must not have a self ${"type"}""" - - val explanation = { - val untpd.ModuleDef(name, tmpl) = mdef - val ValDef(_, selfTpt, _) = tmpl.self - hl"""|${"object"}s must not have a self ${"type"}: - | - |Consider these alternative solutions: - | - Create a trait or a class instead of an object - | - Let the object extend a trait containing the self type: - | - | object $name extends ${selfTpt.show}""" - } - } - - case class TupleTooLong(ts: List[untpd.Tree])(implicit ctx: Context) - extends Message(13) { - import Definitions.MaxTupleArity - val kind = "Syntax" - val msg = hl"""A ${"tuple"} cannot have more than ${MaxTupleArity} members""" - - val explanation = { - val members = ts.map(_.showSummary).grouped(MaxTupleArity) - val nestedRepresentation = members.map(_.mkString(", ")).mkString(")(") - hl"""|This restriction will be removed in the future. - |Currently it is possible to use nested tuples when more than $MaxTupleArity are needed, for example: - | - |((${nestedRepresentation}))""" - } - } - - case class RepeatedModifier(modifier: String)(implicit ctx:Context) - extends Message(14) { - val kind = "Syntax" - val msg = hl"""repeated modifier $modifier""" - - val explanation = { - val code1 = hl"""private private val Origin = Point(0, 0)""" - val code2 = hl"""private final val Origin = Point(0, 0)""" - hl"""This happens when you accidentally specify the same modifier twice. - | - |Example: - | - |$code1 - | - |instead of - | - |$code2 - | - |""" - } - } - - case class InterpolatedStringError()(implicit ctx:Context) - extends Message(15) { - val kind = "Syntax" - val msg = "error in interpolated string: identifier or block expected" - val explanation = { - val code1 = "s\"$new Point(0, 0)\"" - val code2 = "s\"${new Point(0, 0)}\"" - hl"""|This usually happens when you forget to place your expressions inside curly braces. - | - |$code1 - | - |should be written as - | - |$code2 - |""" - } - } - - case class UnboundPlaceholderParameter()(implicit ctx:Context) - extends Message(16) { - val kind = "Syntax" - val msg = "unbound placeholder parameter; incorrect use of `_`" - val explanation = - hl"""|The `_` placeholder syntax was used where it could not be bound. - |Consider explicitly writing the variable binding. - | - |This can be done by replacing `_` with a variable (eg. `x`) - |and adding ${"x =>"} where applicable. - | - |Example before: - | - |${"{ _ }"} - | - |Example after: - | - |${"x => { x }"} - | - |Another common occurrence for this error is defining a val with `_`: - | - |${"val a = _"} - | - |But this val definition isn't very useful, it can never be assigned - |another value. And thus will always remain uninitialized. - |Consider replacing the ${"val"} with ${"var"}: - | - |${"var a = _"} - | - |Note that this use of `_` is not placeholder syntax, - |but an uninitialized var definition""" - } - - case class IllegalStartSimpleExpr(illegalToken: String)(implicit ctx: Context) - extends Message(17) { - val kind = "Syntax" - val msg = "illegal start of simple expression" - val explanation = { - hl"""|An expression yields a value. In the case of the simple expression, this error - |commonly occurs when there's a missing parenthesis or brace. The reason being - |that a simple expression is one of the following: - | - |- Block - |- Expression in parenthesis - |- Identifier - |- Object creation - |- Literal - | - |which cannot start with ${Red(illegalToken)}.""" - } - } - - case class MissingReturnType()(implicit ctx:Context) extends Message(18) { - val kind = "Syntax" - val msg = "missing return type" - val explanation = - hl"""|An abstract declaration must have a return type. For example: - | - |trait Shape { - | def area: Double // abstract declaration returning a ${"Double"} - |}""" - } - - case class YieldOrDoExpectedInForComprehension()(implicit ctx: Context) - extends Message(19) { - val kind = "Syntax" - val msg = hl"${"yield"} or ${"do"} expected" - - val explanation = - hl"""|When the enumerators in a for comprehension are not placed in parentheses or - |braces, a ${"do"} or ${"yield"} statement is required after the enumerators - |section of the comprehension. - | - |You can save some keystrokes by omitting the parentheses and writing - | - |${"val numbers = for i <- 1 to 3 yield i"} - | - | instead of - | - |${"val numbers = for (i <- 1 to 3) yield i"} - | - |but the ${"yield"} keyword is still required. - | - |For comprehensions that simply perform a side effect without yielding anything - |can also be written without parentheses but a ${"do"} keyword has to be - |included. For example, - | - |${"for (i <- 1 to 3) println(i)"} - | - |can be written as - | - |${"for i <- 1 to 3 do println(i) // notice the 'do' keyword"} - | - |""" - } - - case class ProperDefinitionNotFound()(implicit ctx: Context) - extends Message(20) { - val kind = "Definition Not Found" - val msg = hl"""Proper definition was not found in ${"@usecase"}""" - - val explanation = { - val noUsecase = - "def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That" - - val usecase = - """|/** Map from List[A] => List[B] - | * - | * @usecase def map[B](f: A => B): List[B] - | */ - |def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That - |""".stripMargin - - hl"""|Usecases are only supported for ${"def"}s. They exist because with Scala's - |advanced type-system, we sometimes end up with seemingly scary signatures. - |The usage of these methods, however, needs not be - for instance the `map` - |function - | - |${"List(1, 2, 3).map(2 * _) // res: List(2, 4, 6)"} - | - |is easy to understand and use - but has a rather bulky signature: - | - |$noUsecase - | - |to mitigate this and ease the usage of such functions we have the ${"@usecase"} - |annotation for docstrings. Which can be used like this: - | - |$usecase - | - |When creating the docs, the signature of the method is substituted by the - |usecase and the compiler makes sure that it is valid. Because of this, you're - |only allowed to use ${"def"}s when defining usecases.""" - } - } - - case class ByNameParameterNotSupported()(implicit ctx: Context) - extends Message(21) { - val kind = "Syntax" - val msg = "By-name parameter type not allowed here." - - val explanation = - hl"""|By-name parameters act like functions that are only evaluated when referenced, - |allowing for lazy evaluation of a parameter. - | - |An example of using a by-name parameter would look like: - |${"def func(f: => Boolean) = f // 'f' is evaluated when referenced within the function"} - | - |An example of the syntax of passing an actual function as a parameter: - |${"def func(f: (Boolean => Boolean)) = f(true)"} - | - |or: - | - |${"def func(f: Boolean => Boolean) = f(true)"} - | - |And the usage could be as such: - |${"func(bool => // do something...)"} - |""" - } - - case class WrongNumberOfArgs(fntpe: Type, argKind: String, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree])(implicit ctx: Context) - extends Message(22) { - val kind = "Syntax" - - private val expectedCount = expectedArgs.length - private val actualCount = actual.length - private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough" - - //TODO add def simpleParamName to TypeParamInfo - private val expectedArgString = fntpe - .widen.typeParams - .map(_.paramName.unexpandedName.show) - .mkString("[", ", ", "]") - - private val actualArgString = actual.map(_.show).mkString("[", ", ", "]") - - private val prettyName = fntpe.termSymbol match { - case NoSymbol => fntpe.show - case symbol => symbol.showFullName - } - - val msg = - hl"""|${NoColor(msgPrefix)} ${argKind} arguments for $prettyName$expectedArgString - |expected: $expectedArgString - |actual: $actualArgString""".stripMargin - - val explanation = { - val tooManyTypeParams = - """|val tuple2: (Int, String) = (1, "one") - |val list: List[(Int, String)] = List(tuple2)""".stripMargin - - if (actualCount > expectedCount) - hl"""|You have supplied too many type parameters - | - |For example List takes a single type parameter (List[A]) - |If you need to hold more types in a list then you need to combine them - |into another data type that can contain the number of types you need, - |In this example one solution would be to use a Tuple: - | - |${tooManyTypeParams}""" - else - hl"""|You have not supplied enough type parameters - |If you specify one type parameter then you need to specify every type parameter.""" - } - } - - case class IllegalVariableInPatternAlternative()(implicit ctx: Context) - extends Message(23) { - val kind = "Syntax" - val msg = "Variables are not allowed in alternative patterns" - val explanation = { - val varInAlternative = - """|def g(pair: (Int,Int)): Int = pair match { - | case (1, n) | (n, 1) => n - | case _ => 0 - |}""".stripMargin - - val fixedVarInAlternative = - """|def g(pair: (Int,Int)): Int = pair match { - | case (1, n) => n - | case (n, 1) => n - | case _ => 0 - |}""".stripMargin - - hl"""|Variables are not allowed within alternate pattern matches. You can workaround - |this issue by adding additional cases for each alternative. For example, the - |illegal function: - | - |$varInAlternative - |could be implemented by moving each alternative into a separate case: - | - |$fixedVarInAlternative""" - } - } - - case class TypeParamsTypeExpected(mods: untpd.Modifiers, identifier: TermName)(implicit ctx: Context) - extends Message(24) { - val kind = "Syntax" - val msg = hl"""Expected ${"type"} keyword for type parameter $identifier""" - val explanation = - hl"""|This happens when you add modifiers like ${"private"} or ${"protected"} - |to your type parameter definition without adding the ${"type"} keyword. - | - |Add ${"type"} to your code, e.g.: - |${s"trait A[${mods.flags} type $identifier]"} - |""" - } - - case class IdentifierExpected(identifier: String)(implicit ctx: Context) - extends Message(25) { - val kind = "Syntax" - val msg = "identifier expected" - val explanation = { - val wrongIdentifier = s"def foo: $identifier = {...}" - val validIdentifier = s"def foo = {...}" - hl"""|An identifier expected, but `$identifier` found. This could be because - |`$identifier` is not a valid identifier. As a workaround, the compiler could - |infer the type for you. For example, instead of: - | - |$wrongIdentifier - | - |Write your code like: - | - |$validIdentifier - | - |""" - } - } - - case class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) - extends Message(26) { - val kind = "Syntax" - val msg = "auxiliary constructor needs non-implicit parameter list" - val explanation = - hl"""|Only the primary constructor is allowed an ${"implicit"} parameter list; - |auxiliary constructors need non-implicit parameter lists. When a primary - |constructor has an implicit argslist, auxiliary constructors that call the - |primary constructor must specify the implicit value. - | - |To resolve this issue check for: - | - forgotten parenthesis on ${"this"} (${"def this() = { ... }"}) - | - auxiliary constructors specify the implicit value - |""" - } - - case class IncorrectRepeatedParameterSyntax()(implicit ctx: Context) extends Message(27) { - val kind = "Syntax" - val msg = "'*' expected" - val explanation = - hl"""|Expected * in '_*' operator. - | - |The '_*' operator can be used to supply a sequence-based argument - |to a method with a variable-length or repeated parameter. It is used - |to expand the sequence to a variable number of arguments, such that: - |func(args: _*) would expand to func(arg1, arg2 ... argN). - | - |Below is an example of how a method with a variable-length - |parameter can be declared and used. - | - |Squares the arguments of a variable-length parameter: - |${"def square(args: Int*) = args.map(a => a * a)"} - | - |Usage: - |${"square(1, 2, 3) // res0: List[Int] = List(1, 4, 9)"} - | - |Secondary Usage with '_*': - |${"val ints = List(2, 3, 4) // ints: List[Int] = List(2, 3, 4)"} - |${"square(ints: _*) // res1: List[Int] = List(4, 9, 16)"} - |""".stripMargin - } - - case class IllegalLiteral()(implicit ctx: Context) extends Message(28) { - val kind = "Syntax" - val msg = "illegal literal" - val explanation = - hl"""|Available literals can be divided into several groups: - | - Integer literals: 0, 21, 0xFFFFFFFF, -42L - | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1 - | - Boolean Literals: true, false - | - Character Literals: 'a', '\u0041', '\n' - | - String Literals: "Hello, World!" - | - null - |""" - } - - case class PatternMatchExhaustivity(uncovered: String)(implicit ctx: Context) - extends Message(29) { - val kind = "Pattern Match Exhaustivity" - val msg = - hl"""|match may not be exhaustive. - | - |It would fail on: $uncovered""" - - - val explanation = "" - } - - case class MatchCaseUnreachable()(implicit ctx: Context) - extends Message(30) { - val kind = s"""Match ${hl"case"} Unreachable""" - val msg = "unreachable code" - val explanation = "" - } - - case class SeqWildcardPatternPos()(implicit ctx: Context) - extends Message(31) { - val kind = "Syntax" - val msg = "`_*' can be used only for last argument" - val explanation = { - val code = - """def sumOfTheFirstTwo(list: List[Int]): Int = list match { - | case List(first, second, x:_*) => first + second - | case _ => 0 - |}""" - hl"""|Sequence wildcard pattern is expected at the end of an argument list. - |This pattern matches any remaining elements in a sequence. - |Consider the following example: - | - |$code - | - |Calling: - | - |${"sumOfTheFirstTwo(List(1, 2, 10))"} - | - |would give 3 as a result""" - } - } - - case class IllegalStartOfSimplePattern()(implicit ctx: Context) extends Message(32) { - val kind = "Syntax" - val msg = "illegal start of simple pattern" - val explanation = { - val sipCode = - """def f(x: Int, y: Int) = x match { - | case `y` => ... - |} - """ - val constructorPatternsCode = - """case class Person(name: String, age: Int) - | - |def test(p: Person) = p match { - | case Person(name, age) => ... - |} - """ - val tupplePatternsCode = - """def swap(tuple: (String, Int)): (Int, String) = tuple match { - | case (text, number) => (number, text) - |} - """ - val patternSequencesCode = - """def getSecondValue(list: List[Int]): Int = list match { - | case List(_, second, x:_*) => second - | case _ => 0 - |}""" - hl"""|Simple patterns can be divided into several groups: - |- Variable Patterns: ${"case x => ..."}. - | It matches any value, and binds the variable name to that value. - | A special case is the wild-card pattern _ which is treated as if it was a fresh - | variable on each occurrence. - | - |- Typed Patterns: ${"case x: Int => ..."} or ${"case _: Int => ..."}. - | This pattern matches any value matched by the specified type; it binds the variable - | name to that value. - | - |- Literal Patterns: ${"case 123 => ..."} or ${"case 'A' => ..."}. - | This type of pattern matches any value that is equal to the specified literal. - | - |- Stable Identifier Patterns: - | - | $sipCode - | - | the match succeeds only if the x argument and the y argument of f are equal. - | - |- Constructor Patterns: - | - | $constructorPatternsCode - | - | The pattern binds all object's fields to the variable names (name and age, in this - | case). - | - |- Tuple Patterns: - | - | $tupplePatternsCode - | - | Calling: - | - | ${"""swap(("Luftballons", 99)"""} - | - | would give ${"""(99, "Luftballons")"""} as a result. - | - |- Pattern Sequences: - | - | $patternSequencesCode - | - | Calling: - | - | ${"getSecondValue(List(1, 10, 2))"} - | - | would give 10 as a result. - | This pattern is possible because a companion object for the List class has a method - | with the following signature: - | - | ${"def unapplySeq[A](x: List[A]): Some[List[A]]"} - |""" - } - } - - case class PkgDuplicateSymbol(existing: Symbol)(implicit ctx: Context) - extends Message(33) { - val kind = "Duplicate Symbol" - val msg = hl"trying to define package with same name as `$existing`" - val explanation = "" - } -} diff --git a/src/dotty/tools/dotc/rewrite/Rewrites.scala b/src/dotty/tools/dotc/rewrite/Rewrites.scala deleted file mode 100644 index c42c808fe..000000000 --- a/src/dotty/tools/dotc/rewrite/Rewrites.scala +++ /dev/null @@ -1,92 +0,0 @@ -package dotty.tools.dotc -package rewrite - -import util.{SourceFile, Positions} -import Positions.Position -import core.Contexts.{Context, FreshContext} -import collection.mutable - -/** Handles rewriting of Scala2 files to Dotty */ -object Rewrites { - private class PatchedFiles extends mutable.HashMap[SourceFile, Patches] - - private case class Patch(pos: Position, replacement: String) { - def delta = replacement.length - (pos.end - pos.start) - } - - private class Patches(source: SourceFile) { - private val pbuf = new mutable.ListBuffer[Patch]() - - def addPatch(pos: Position, replacement: String): Unit = - pbuf += Patch(pos, replacement) - - def apply(cs: Array[Char]): Array[Char] = { - val delta = pbuf.map(_.delta).sum - val patches = pbuf.toList.sortBy(_.pos.start) - if (patches.nonEmpty) - patches reduceLeft {(p1, p2) => - assert(p1.pos.end <= p2.pos.start, s"overlapping patches: $p1 and $p2") - p2 - } - val ds = new Array[Char](cs.length + delta) - def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = { - def copy(upTo: Int): Int = { - val untouched = upTo - inIdx - Array.copy(cs, inIdx, ds, outIdx, untouched) - outIdx + untouched - } - ps match { - case patch @ Patch(pos, replacement) :: ps1 => - val outNew = copy(pos.start) - replacement.copyToArray(ds, outNew) - loop(ps1, pos.end, outNew + replacement.length) - case Nil => - val outNew = copy(cs.length) - assert(outNew == ds.length, s"$outNew != ${ds.length}") - } - } - loop(patches, 0, 0) - ds - } - - def writeBack(): Unit = { - val out = source.file.output - val chars = apply(source.underlying.content) - val bytes = new String(chars).getBytes - out.write(bytes) - out.close() - } - } - - /** If -rewrite is set, record a patch that replaces the range - * given by `pos` in `source` by `replacement` - */ - def patch(source: SourceFile, pos: Position, replacement: String)(implicit ctx: Context): Unit = - for (rewrites <- ctx.settings.rewrite.value) - rewrites.patched - .getOrElseUpdate(source, new Patches(source)) - .addPatch(pos, replacement) - - /** Patch position in `ctx.compilationUnit.source`. */ - def patch(pos: Position, replacement: String)(implicit ctx: Context): Unit = - patch(ctx.compilationUnit.source, pos, replacement) - - /** If -rewrite is set, apply all patches and overwrite patched source files. - */ - def writeBack()(implicit ctx: Context) = - for (rewrites <- ctx.settings.rewrite.value; source <- rewrites.patched.keys) { - ctx.echo(s"[patched file ${source.file.path}]") - rewrites.patched(source).writeBack() - } -} - -/** A completely encapsulated class representing rewrite state, used - * as an optional setting. - */ -class Rewrites { - import Rewrites._ - private val patched = new PatchedFiles -} - - - diff --git a/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/src/dotty/tools/dotc/sbt/ExtractAPI.scala deleted file mode 100644 index bc8528c05..000000000 --- a/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ /dev/null @@ -1,518 +0,0 @@ -package dotty.tools.dotc -package sbt - -import ast.{Trees, tpd} -import core._, core.Decorators._ -import Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._ -import Names._, NameOps._, StdNames._ -import typer.Inliner - -import dotty.tools.io.Path -import java.io.PrintWriter - -import scala.collection.mutable - -/** This phase sends a representation of the API of classes to sbt via callbacks. - * - * This is used by sbt for incremental recompilation. - * - * See the documentation of `ExtractAPICollector`, `ExtractDependencies`, - * `ExtractDependenciesCollector` and - * http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html for more - * information on incremental recompilation. - * - * The following flags affect this phase: - * -Yforce-sbt-phases - * -Ydump-sbt-inc - * - * @see ExtractDependencies - */ -class ExtractAPI extends Phase { - override def phaseName: String = "sbt-api" - - // SuperAccessors need to be part of the API (see the scripted test - // `trait-super` for an example where this matters), this is only the case - // after `PostTyper` (unlike `ExtractDependencies`, the simplication to trees - // done by `PostTyper` do not affect this phase because it only cares about - // definitions, and `PostTyper` does not change definitions). - override def runsAfter = Set(classOf[transform.PostTyper]) - - override def run(implicit ctx: Context): Unit = { - val unit = ctx.compilationUnit - val dumpInc = ctx.settings.YdumpSbtInc.value - val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value - if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) { - val sourceFile = unit.source.file.file - val apiTraverser = new ExtractAPICollector - val source = apiTraverser.apiSource(unit.tpdTree) - - if (dumpInc) { - // Append to existing file that should have been created by ExtractDependencies - val pw = new PrintWriter(Path(sourceFile).changeExtension("inc").toFile - .bufferedWriter(append = true), true) - try { - pw.println(DefaultShowAPI(source)) - } finally pw.close() - } - - if (ctx.sbtCallback != null) - ctx.sbtCallback.api(sourceFile, source) - } - } -} - -/** Extracts full (including private members) API representation out of Symbols and Types. - * - * The exact representation used for each type is not important: the only thing - * that matters is that a binary-incompatible or source-incompatible change to - * the API (for example, changing the signature of a method, or adding a parent - * to a class) should result in a change to the API representation so that sbt - * can recompile files that depend on this API. - * - * Note that we only records types as they are defined and never "as seen from" - * some other prefix because `Types#asSeenFrom` is a complex operation and - * doing it for every inherited member would be slow, and because the number - * of prefixes can be enormous in some cases: - * - * class Outer { - * type T <: S - * type S - * class A extends Outer { /*...*/ } - * class B extends Outer { /*...*/ } - * class C extends Outer { /*...*/ } - * class D extends Outer { /*...*/ } - * class E extends Outer { /*...*/ } - * } - * - * `S` might be refined in an arbitrary way inside `A` for example, this - * affects the type of `T` as seen from `Outer#A`, so we could record that, but - * the class `A` also contains itself as a member, so `Outer#A#A#A#...` is a - * valid prefix for `T`. Even if we avoid loops, we still have a combinatorial - * explosion of possible prefixes, like `Outer#A#B#C#D#E`. - * - * It is much simpler to record `T` once where it is defined, but that means - * that the API representation of `T` may not change even though `T` as seen - * from some prefix has changed. This is why in `ExtractDependencies` we need - * to traverse used types to not miss dependencies, see the documentation of - * `ExtractDependencies#usedTypeTraverser`. - * - * TODO: sbt does not store the full representation that we compute, instead it - * hashes parts of it to reduce memory usage, then to see if something changed, - * it compares the hashes instead of comparing the representations. We should - * investigate whether we can just directly compute hashes in this phase - * without going through an intermediate representation, see - * http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html#Hashing+an+API+representation - */ -private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder { - import tpd._ - import xsbti.api - - /** This cache is necessary for correctness, see the comment about inherited - * members in `apiClassStructure` - */ - private[this] val classLikeCache = new mutable.HashMap[ClassSymbol, api.ClassLike] - /** This cache is optional, it avoids recomputing representations */ - private[this] val typeCache = new mutable.HashMap[Type, api.Type] - - private[this] object Constants { - val emptyStringArray = Array[String]() - val local = new api.ThisQualifier - val public = new api.Public - val privateLocal = new api.Private(local) - val protectedLocal = new api.Protected(local) - val unqualified = new api.Unqualified - val thisPath = new api.This - val emptyType = new api.EmptyType - val emptyModifiers = - new api.Modifiers(false, false, false, false, false,false, false, false) - } - - /** Some Dotty types do not have a corresponding type in xsbti.api.* that - * represents them. Until this is fixed we can workaround this by using - * special annotations that can never appear in the source code to - * represent these types. - * - * @param tp An approximation of the type we're trying to represent - * @param marker A special annotation to differentiate our type - */ - private def withMarker(tp: api.Type, marker: api.Annotation) = - new api.Annotated(tp, Array(marker)) - private def marker(name: String) = - new api.Annotation(new api.Constant(Constants.emptyType, name), Array()) - val orMarker = marker("Or") - val byNameMarker = marker("ByName") - - - /** Extract the API representation of a source file */ - def apiSource(tree: Tree): api.SourceAPI = { - val classes = new mutable.ListBuffer[api.ClassLike] - def apiClasses(tree: Tree): Unit = tree match { - case PackageDef(_, stats) => - stats.foreach(apiClasses) - case tree: TypeDef => - classes += apiClass(tree.symbol.asClass) - case _ => - } - - apiClasses(tree) - forceThunks() - new api.SourceAPI(Array(), classes.toArray) - } - - def apiClass(sym: ClassSymbol): api.ClassLike = - classLikeCache.getOrElseUpdate(sym, computeClass(sym)) - - private def computeClass(sym: ClassSymbol): api.ClassLike = { - import xsbti.api.{DefinitionType => dt} - val defType = - if (sym.is(Trait)) dt.Trait - else if (sym.is(ModuleClass)) { - if (sym.is(PackageClass)) dt.PackageModule - else dt.Module - } else dt.ClassDef - - val selfType = apiType(sym.classInfo.givenSelfType) - - val name = if (sym.is(ModuleClass)) sym.fullName.sourceModuleName else sym.fullName - - val tparams = sym.typeParams.map(apiTypeParameter) - - val structure = apiClassStructure(sym) - - new api.ClassLike( - defType, strict2lzy(selfType), strict2lzy(structure), Constants.emptyStringArray, - tparams.toArray, name.toString, apiAccess(sym), apiModifiers(sym), - apiAnnotations(sym).toArray) - } - - private[this] val LegacyAppClass = ctx.requiredClass("dotty.runtime.LegacyApp") - - def apiClassStructure(csym: ClassSymbol): api.Structure = { - val cinfo = csym.classInfo - - val bases = linearizedAncestorTypes(cinfo) - val apiBases = bases.map(apiType) - - // Synthetic methods that are always present do not affect the API - // and can therefore be ignored. - def alwaysPresent(s: Symbol) = - s.isCompanionMethod || (csym.is(ModuleClass) && s.isConstructor) - val decls = cinfo.decls.filterNot(alwaysPresent).toList - val apiDecls = apiDefinitions(decls) - - val declSet = decls.toSet - // TODO: We shouldn't have to compute inherited members. Instead, `Structure` - // should have a lazy `parentStructures` field. - val inherited = cinfo.baseClasses - // We cannot filter out `LegacyApp` because it contains the main method, - // see the comment about main class discovery in `computeType`. - .filter(bc => !bc.is(Scala2x) || bc.eq(LegacyAppClass)) - .flatMap(_.classInfo.decls.filterNot(s => s.is(Private) || declSet.contains(s))) - // Inherited members need to be computed lazily because a class might contain - // itself as an inherited member, like in `class A { class B extends A }`, - // this works because of `classLikeCache` - val apiInherited = lzy(apiDefinitions(inherited).toArray) - - new api.Structure(strict2lzy(apiBases.toArray), strict2lzy(apiDecls.toArray), apiInherited) - } - - def linearizedAncestorTypes(info: ClassInfo): List[Type] = { - val ref = info.fullyAppliedRef - // Note that the ordering of classes in `baseClasses` is important. - info.baseClasses.tail.map(ref.baseTypeWithArgs) - } - - def apiDefinitions(defs: List[Symbol]): List[api.Definition] = { - // The hash generated by sbt for definitions is supposed to be symmetric so - // we shouldn't have to sort them, but it actually isn't symmetric for - // definitions which are classes, therefore we need to sort classes to - // ensure a stable hash. - // Modules and classes come first and are sorted by name, all other - // definitions come later and are not sorted. - object classFirstSort extends Ordering[Symbol] { - override def compare(a: Symbol, b: Symbol) = { - val aIsClass = a.isClass - val bIsClass = b.isClass - if (aIsClass == bIsClass) { - if (aIsClass) { - if (a.is(Module) == b.is(Module)) - a.fullName.toString.compareTo(b.fullName.toString) - else if (a.is(Module)) - -1 - else - 1 - } else - 0 - } else if (aIsClass) - -1 - else - 1 - } - } - - defs.sorted(classFirstSort).map(apiDefinition) - } - - def apiDefinition(sym: Symbol): api.Definition = { - if (sym.isClass) { - apiClass(sym.asClass) - } else if (sym.isType) { - apiTypeMember(sym.asType) - } else if (sym.is(Mutable, butNot = Accessor)) { - new api.Var(apiType(sym.info), sym.name.toString, - apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray) - } else if (sym.isStable) { - new api.Val(apiType(sym.info), sym.name.toString, - apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray) - } else { - apiDef(sym.asTerm) - } - } - - def apiDef(sym: TermSymbol): api.Def = { - def paramLists(t: Type, start: Int = 0): List[api.ParameterList] = t match { - case pt: PolyType => - assert(start == 0) - paramLists(pt.resultType) - case mt @ MethodType(pnames, ptypes) => - // TODO: We shouldn't have to work so hard to find the default parameters - // of a method, Dotty should expose a convenience method for that, see #1143 - val defaults = - if (sym.is(DefaultParameterized)) { - val qual = - if (sym.isClassConstructor) - sym.owner.companionModule // default getters for class constructors are found in the companion object - else - sym.owner - (0 until pnames.length).map(i => qual.info.member(sym.name.defaultGetterName(start + i)).exists) - } else - (0 until pnames.length).map(Function.const(false)) - val params = (pnames, ptypes, defaults).zipped.map((pname, ptype, isDefault) => - new api.MethodParameter(pname.toString, apiType(ptype), - isDefault, api.ParameterModifier.Plain)) - new api.ParameterList(params.toArray, mt.isImplicit) :: paramLists(mt.resultType, params.length) - case _ => - Nil - } - - val tparams = sym.info match { - case pt: PolyType => - (pt.paramNames, pt.paramBounds).zipped.map((pname, pbounds) => - apiTypeParameter(pname.toString, 0, pbounds.lo, pbounds.hi)) - case _ => - Nil - } - val vparamss = paramLists(sym.info) - val retTp = sym.info.finalResultType.widenExpr - - new api.Def(vparamss.toArray, apiType(retTp), tparams.toArray, - sym.name.toString, apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray) - } - - def apiTypeMember(sym: TypeSymbol): api.TypeMember = { - val typeParams = Array[api.TypeParameter]() - val name = sym.name.toString - val access = apiAccess(sym) - val modifiers = apiModifiers(sym) - val as = apiAnnotations(sym) - val tpe = sym.info - - if (sym.isAliasType) - new api.TypeAlias(apiType(tpe.bounds.hi), typeParams, name, access, modifiers, as.toArray) - else { - assert(sym.isAbstractType) - new api.TypeDeclaration(apiType(tpe.bounds.lo), apiType(tpe.bounds.hi), typeParams, name, access, modifiers, as.to) - } - } - - def apiType(tp: Type): api.Type = { - typeCache.getOrElseUpdate(tp, computeType(tp)) - } - - private def computeType(tp: Type): api.Type = { - // TODO: Never dealias. We currently have to dealias because - // sbt main class discovery relies on the signature of the main - // method being fully dealiased. See https://github.com/sbt/zinc/issues/102 - val tp2 = if (!tp.isHK) tp.dealias else tp - tp2 match { - case NoPrefix | NoType => - Constants.emptyType - case tp: NamedType => - val sym = tp.symbol - // Normalize package prefix to avoid instability of representation - val prefix = if (sym.isClass && sym.owner.is(Package)) - sym.owner.thisType - else - tp.prefix - new api.Projection(simpleType(prefix), sym.name.toString) - case TypeApplications.AppliedType(tycon, args) => - def processArg(arg: Type): api.Type = arg match { - case arg @ TypeBounds(lo, hi) => // Handle wildcard parameters - if (lo.eq(defn.NothingType) && hi.eq(defn.AnyType)) - Constants.emptyType - else { - val name = "_" - val ref = new api.ParameterRef(name) - new api.Existential(ref, - Array(apiTypeParameter(name, arg.variance, lo, hi))) - } - case _ => - apiType(arg) - } - - val apiTycon = simpleType(tycon) - val apiArgs = args.map(processArg) - new api.Parameterized(apiTycon, apiArgs.toArray) - case PolyType(tparams, res) => - val apiTparams = tparams.map(apiTypeParameter) - val apiRes = apiType(res) - new api.Polymorphic(apiRes, apiTparams.toArray) - case rt: RefinedType => - val name = rt.refinedName.toString - val parent = apiType(rt.parent) - - def typeRefinement(name: String, tp: TypeBounds): api.TypeMember = tp match { - case TypeAlias(alias) => - new api.TypeAlias(apiType(alias), - Array(), name, Constants.public, Constants.emptyModifiers, Array()) - case TypeBounds(lo, hi) => - new api.TypeDeclaration(apiType(lo), apiType(hi), - Array(), name, Constants.public, Constants.emptyModifiers, Array()) - } - - val decl: Array[api.Definition] = rt.refinedInfo match { - case rinfo: TypeBounds => - Array(typeRefinement(name, rinfo)) - case _ => - ctx.debuglog(i"sbt-api: skipped structural refinement in $rt") - Array() - } - new api.Structure(strict2lzy(Array(parent)), strict2lzy(decl), strict2lzy(Array())) - case tp: RecType => - apiType(tp.parent) - case RecThis(recType) => - // `tp` must be present inside `recType`, so calling `apiType` on - // `recType` would lead to an infinite recursion, we avoid this by - // computing the representation of `recType` lazily. - apiLazy(recType) - case tp: AndOrType => - val parents = List(apiType(tp.tp1), apiType(tp.tp2)) - - // TODO: Add a real representation for AndOrTypes in xsbti. The order of - // types in an `AndOrType` does not change the API, so the API hash should - // be symmetric. - val s = new api.Structure(strict2lzy(parents.toArray), strict2lzy(Array()), strict2lzy(Array())) - if (tp.isAnd) - s - else - withMarker(s, orMarker) - case ExprType(resultType) => - withMarker(apiType(resultType), byNameMarker) - case ConstantType(constant) => - new api.Constant(apiType(constant.tpe), constant.stringValue) - case AnnotatedType(tpe, annot) => - // TODO: Annotation support - ctx.debuglog(i"sbt-api: skipped annotation in $tp2") - apiType(tpe) - case tp: ThisType => - apiThis(tp.cls) - case tp: ParamType => - // TODO: Distinguishing parameters based on their names alone is not enough, - // the binder is also needed (at least for type lambdas). - new api.ParameterRef(tp.paramName.toString) - case tp: LazyRef => - apiType(tp.ref) - case tp: TypeVar => - apiType(tp.underlying) - case _ => { - ctx.warning(i"sbt-api: Unhandled type ${tp.getClass} : $tp") - Constants.emptyType - } - } - } - - // TODO: Get rid of this method. See https://github.com/sbt/zinc/issues/101 - def simpleType(tp: Type): api.SimpleType = apiType(tp) match { - case tp: api.SimpleType => - tp - case _ => - ctx.debuglog("sbt-api: Not a simple type: " + tp.show) - Constants.emptyType - } - - def apiLazy(tp: => Type): api.Type = { - // TODO: The sbt api needs a convenient way to make a lazy type. - // For now, we repurpose Structure for this. - val apiTp = lzy(Array(apiType(tp))) - new api.Structure(apiTp, strict2lzy(Array()), strict2lzy(Array())) - } - - def apiThis(sym: Symbol): api.Singleton = { - val pathComponents = sym.ownersIterator.takeWhile(!_.isEffectiveRoot) - .map(s => new api.Id(s.name.toString)) - new api.Singleton(new api.Path(pathComponents.toArray.reverse ++ Array(Constants.thisPath))) - } - - def apiTypeParameter(tparam: TypeParamInfo): api.TypeParameter = - apiTypeParameter(tparam.paramName.toString, tparam.paramVariance, - tparam.paramBounds.lo, tparam.paramBounds.hi) - - def apiTypeParameter(name: String, variance: Int, lo: Type, hi: Type): api.TypeParameter = - new api.TypeParameter(name, Array(), Array(), apiVariance(variance), - apiType(lo), apiType(hi)) - - def apiVariance(v: Int): api.Variance = { - import api.Variance._ - if (v < 0) Contravariant - else if (v > 0) Covariant - else Invariant - } - - def apiAccess(sym: Symbol): api.Access = { - // Symbols which are private[foo] do not have the flag Private set, - // but their `privateWithin` exists, see `Parsers#ParserCommon#normalize`. - if (!sym.is(Protected | Private) && !sym.privateWithin.exists) - Constants.public - else if (sym.is(PrivateLocal)) - Constants.privateLocal - else if (sym.is(ProtectedLocal)) - Constants.protectedLocal - else { - val qualifier = - if (sym.privateWithin eq NoSymbol) - Constants.unqualified - else - new api.IdQualifier(sym.privateWithin.fullName.toString) - if (sym.is(Protected)) - new api.Protected(qualifier) - else - new api.Private(qualifier) - } - } - - def apiModifiers(sym: Symbol): api.Modifiers = { - val absOver = sym.is(AbsOverride) - val abs = sym.is(Abstract) || sym.is(Deferred) || absOver - val over = sym.is(Override) || absOver - new api.Modifiers(abs, over, sym.is(Final), sym.is(Sealed), - sym.is(Implicit), sym.is(Lazy), sym.is(Macro), sym.is(SuperAccessor)) - } - - // TODO: Support other annotations - def apiAnnotations(s: Symbol): List[api.Annotation] = { - val annots = new mutable.ListBuffer[api.Annotation] - - if (Inliner.hasBodyToInline(s)) { - // FIXME: If the body of an inline method changes, all the reverse - // dependencies of this method need to be recompiled. sbt has no way - // of tracking method bodies, so as a hack we include the pretty-printed - // typed tree of the method as part of the signature we send to sbt. - // To do this properly we would need a way to hash trees and types in - // dotty itself. - val printTypesCtx = ctx.fresh.setSetting(ctx.settings.printtypes, true) - annots += marker(Inliner.bodyToInline(s).show(printTypesCtx).toString) - } - - annots.toList - } -} diff --git a/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/src/dotty/tools/dotc/sbt/ExtractDependencies.scala deleted file mode 100644 index 229e35360..000000000 --- a/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ /dev/null @@ -1,268 +0,0 @@ -package dotty.tools.dotc -package sbt - -import ast.{Trees, tpd} -import core._, core.Decorators._ -import Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._ -import Names._, NameOps._, StdNames._ - -import scala.collection.{Set, mutable} - -import dotty.tools.io.{AbstractFile, Path, PlainFile, ZipArchive} -import java.io.File - -import java.util.{Arrays, Comparator} - -import xsbti.DependencyContext - -/** This phase sends information on classes' dependencies to sbt via callbacks. - * - * This is used by sbt for incremental recompilation. Briefly, when a file - * changes sbt will recompile it, if its API has changed (determined by what - * `ExtractAPI` sent) then sbt will determine which reverse-dependencies - * (determined by what `ExtractDependencies` sent) of the API have to be - * recompiled depending on what changed. - * - * See the documentation of `ExtractDependenciesCollector`, `ExtractAPI`, - * `ExtractAPICollector` and - * http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html for more - * information on how sbt incremental compilation works. - * - * The following flags affect this phase: - * -Yforce-sbt-phases - * -Ydump-sbt-inc - * - * @see ExtractAPI - */ -class ExtractDependencies extends Phase { - override def phaseName: String = "sbt-deps" - - // This phase should be run directly after `Frontend`, if it is run after - // `PostTyper`, some dependencies will be lost because trees get simplified. - // See the scripted test `constants` for an example where this matters. - // TODO: Add a `Phase#runsBefore` method ? - - override def run(implicit ctx: Context): Unit = { - val unit = ctx.compilationUnit - val dumpInc = ctx.settings.YdumpSbtInc.value - val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value - if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) { - val sourceFile = unit.source.file.file - val extractDeps = new ExtractDependenciesCollector - extractDeps.traverse(unit.tpdTree) - - if (dumpInc) { - val names = extractDeps.usedNames.map(_.toString).toArray[Object] - val deps = extractDeps.topLevelDependencies.map(_.toString).toArray[Object] - val inhDeps = extractDeps.topLevelInheritanceDependencies.map(_.toString).toArray[Object] - Arrays.sort(names) - Arrays.sort(deps) - Arrays.sort(inhDeps) - - val pw = Path(sourceFile).changeExtension("inc").toFile.printWriter() - try { - pw.println(s"// usedNames: ${names.mkString(",")}") - pw.println(s"// topLevelDependencies: ${deps.mkString(",")}") - pw.println(s"// topLevelInheritanceDependencies: ${inhDeps.mkString(",")}") - } finally pw.close() - } - - if (ctx.sbtCallback != null) { - extractDeps.usedNames.foreach(name => - ctx.sbtCallback.usedName(sourceFile, name.toString)) - extractDeps.topLevelDependencies.foreach(dep => - recordDependency(sourceFile, dep, DependencyContext.DependencyByMemberRef)) - extractDeps.topLevelInheritanceDependencies.foreach(dep => - recordDependency(sourceFile, dep, DependencyContext.DependencyByInheritance)) - } - } - } - - /** Record that `currentSourceFile` depends on the file where `dep` was loaded from. - * - * @param currentSourceFile The source file of the current unit - * @param dep The dependency - * @param context Describes how `currentSourceFile` depends on `dep` - */ - def recordDependency(currentSourceFile: File, dep: Symbol, context: DependencyContext) - (implicit ctx: Context) = { - val depFile = dep.associatedFile - if (depFile != null) { - if (depFile.path.endsWith(".class")) { - /** Transform `List(java, lang, String.class)` into `java.lang.String` */ - def className(classSegments: List[String]) = - classSegments.mkString(".").stripSuffix(".class") - def binaryDependency(file: File, className: String) = - ctx.sbtCallback.binaryDependency(file, className, currentSourceFile, context) - - depFile match { - case ze: ZipArchive#Entry => - for (zip <- ze.underlyingSource; zipFile <- Option(zip.file)) { - val classSegments = Path(ze.path).segments - binaryDependency(zipFile, className(classSegments)) - } - case pf: PlainFile => - val packages = dep.ownersIterator - .filter(x => x.is(PackageClass) && !x.isEffectiveRoot).length - // We can recover the fully qualified name of a classfile from - // its path - val classSegments = pf.givenPath.segments.takeRight(packages + 1) - binaryDependency(pf.file, className(classSegments)) - case _ => - } - } else if (depFile.file != currentSourceFile) { - ctx.sbtCallback.sourceDependency(depFile.file, currentSourceFile, context) - } - } - } -} - -/** Extract the dependency information of a compilation unit. - * - * To understand why we track the used names see the section "Name hashing - * algorithm" in http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html - * To understand why we need to track dependencies introduced by inheritance - * specially, see the subsection "Dependencies introduced by member reference and - * inheritance" in the "Name hashing algorithm" section. - */ -private class ExtractDependenciesCollector(implicit val ctx: Context) extends tpd.TreeTraverser { - import tpd._ - - private[this] val _usedNames = new mutable.HashSet[Name] - private[this] val _topLevelDependencies = new mutable.HashSet[Symbol] - private[this] val _topLevelInheritanceDependencies = new mutable.HashSet[Symbol] - - /** The names used in this class, this does not include names which are only - * defined and not referenced. - */ - def usedNames: Set[Name] = _usedNames - - /** The set of top-level classes that the compilation unit depends on - * because it refers to these classes or something defined in them. - * This is always a superset of `topLevelInheritanceDependencies` by definition. - */ - def topLevelDependencies: Set[Symbol] = _topLevelDependencies - - /** The set of top-level classes that the compilation unit extends or that - * contain a non-top-level class that the compilaion unit extends. - */ - def topLevelInheritanceDependencies: Set[Symbol] = _topLevelInheritanceDependencies - - private def addUsedName(name: Name) = - _usedNames += name - - private def addDependency(sym: Symbol): Unit = - if (!ignoreDependency(sym)) { - val tlClass = sym.topLevelClass - if (tlClass.ne(NoSymbol)) // Some synthetic type aliases like AnyRef do not belong to any class - _topLevelDependencies += sym.topLevelClass - addUsedName(sym.name) - } - - private def ignoreDependency(sym: Symbol) = - sym.eq(NoSymbol) || - sym.isEffectiveRoot || - sym.isAnonymousFunction || - sym.isAnonymousClass - - private def addInheritanceDependency(sym: Symbol): Unit = - _topLevelInheritanceDependencies += sym.topLevelClass - - /** Traverse the tree of a source file and record the dependencies which - * can be retrieved using `topLevelDependencies`, `topLevelInheritanceDependencies`, - * and `usedNames` - */ - override def traverse(tree: Tree)(implicit ctx: Context): Unit = { - tree match { - case Import(expr, selectors) => - def lookupImported(name: Name) = expr.tpe.member(name).symbol - def addImported(name: Name) = { - // importing a name means importing both a term and a type (if they exist) - addDependency(lookupImported(name.toTermName)) - addDependency(lookupImported(name.toTypeName)) - } - selectors foreach { - case Ident(name) => - addImported(name) - case Thicket(Ident(name) :: Ident(rename) :: Nil) => - addImported(name) - if (rename ne nme.WILDCARD) - addUsedName(rename) - case _ => - } - case Inlined(call, _, _) => - // The inlined call is normally ignored by TreeTraverser but we need to - // record it as a dependency - traverse(call) - case t: TypeTree => - usedTypeTraverser.traverse(t.tpe) - case ref: RefTree => - addDependency(ref.symbol) - usedTypeTraverser.traverse(ref.tpe) - case t @ Template(_, parents, _, _) => - t.parents.foreach(p => addInheritanceDependency(p.tpe.typeSymbol)) - case _ => - } - traverseChildren(tree) - } - - /** Traverse a used type and record all the dependencies we need to keep track - * of for incremental recompilation. - * - * As a motivating example, given a type `T` defined as: - * - * type T >: L <: H - * type L <: A1 - * type H <: B1 - * class A1 extends A0 - * class B1 extends B0 - * - * We need to record a dependency on `T`, `L`, `H`, `A1`, `B1`. This is - * necessary because the API representation that `ExtractAPI` produces for - * `T` just refers to the strings "L" and "H", it does not contain their API - * representation. Therefore, the name hash of `T` does not change if for - * example the definition of `L` changes. - * - * We do not need to keep track of superclasses like `A0` and `B0` because - * the API representation of a class (and therefore its name hash) already - * contains all necessary information on superclasses. - * - * A natural question to ask is: Since traversing all referenced types to - * find all these names is costly, why not change the API representation - * produced by `ExtractAPI` to contain that information? This way the name - * hash of `T` would change if any of the types it depends on change, and we - * would only need to record a dependency on `T`. Unfortunately there is no - * simple answer to the question "what does T depend on?" because it depends - * on the prefix and `ExtractAPI` does not compute types as seen from every - * possible prefix, the documentation of `ExtractAPI` explains why. - * - * The tests in sbt `types-in-used-names-a`, `types-in-used-names-b`, - * `as-seen-from-a` and `as-seen-from-b` rely on this. - */ - private object usedTypeTraverser extends TypeTraverser { - val seen = new mutable.HashSet[Type] - def traverse(tp: Type): Unit = if (!seen.contains(tp)) { - seen += tp - tp match { - case tp: NamedType => - val sym = tp.symbol - if (!sym.is(Package)) { - addDependency(sym) - if (!sym.isClass) - traverse(tp.info) - traverse(tp.prefix) - } - case tp: ThisType => - traverse(tp.underlying) - case tp: ConstantType => - traverse(tp.underlying) - case tp: MethodParam => - traverse(tp.underlying) - case tp: PolyParam => - traverse(tp.underlying) - case _ => - traverseChildren(tp) - } - } - } -} diff --git a/src/dotty/tools/dotc/sbt/ShowAPI.scala b/src/dotty/tools/dotc/sbt/ShowAPI.scala deleted file mode 100644 index 0e6b19867..000000000 --- a/src/dotty/tools/dotc/sbt/ShowAPI.scala +++ /dev/null @@ -1,156 +0,0 @@ -// This file is copied straight from -// https://github.com/sbt/sbt/blob/0.13/compile/api/src/main/scala/xsbt/api/ShowAPI.scala -// It is convenient to be able to pretty-print the API from Dotty itself to test -// the sbt phase without having to run sbt. - -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package dotty.tools.dotc -package sbt - -import xsbti.api._ - -import scala.util.Try - -object DefaultShowAPI { - private lazy val defaultNesting = Try { java.lang.Integer.parseInt(sys.props.get("sbt.inc.apidiff.depth").get) } getOrElse 2 - - def apply(d: Definition) = ShowAPI.showDefinition(d)(defaultNesting) - def apply(d: Type) = ShowAPI.showType(d)(defaultNesting) - def apply(a: SourceAPI) = ShowAPI.showApi(a)(defaultNesting) -} - -object ShowAPI { - private lazy val numDecls = Try { java.lang.Integer.parseInt(sys.props.get("sbt.inc.apidiff.decls").get) } getOrElse 0 - - private def truncateDecls(decls: Array[Definition]): Array[Definition] = if (numDecls <= 0) decls else decls.take(numDecls) - private def lines(ls: Seq[String]): String = ls.mkString("\n", "\n", "\n") - - def showApi(a: SourceAPI)(implicit nesting: Int) = - a.packages.map(pkg => "package " + pkg.name).mkString("\n") + lines(truncateDecls(a.definitions).map(showDefinition)) - - def showDefinition(d: Definition)(implicit nesting: Int): String = d match { - case v: Val => showMonoDef(v, "val") + ": " + showType(v.tpe) - case v: Var => showMonoDef(v, "var") + ": " + showType(v.tpe) - case d: Def => showPolyDef(d, "def") + showValueParams(d.valueParameters) + ": " + showType(d.returnType) - case ta: TypeAlias => showPolyDef(ta, "type") + " = " + showType(ta.tpe) - case td: TypeDeclaration => showPolyDef(td, "type") + showBounds(td.lowerBound, td.upperBound) - case cl: ClassLike => showPolyDef(cl, showDefinitionType(cl.definitionType)) + " extends " + showTemplate(cl) - } - - private def showTemplate(cl: ClassLike)(implicit nesting: Int) = - if (nesting <= 0) "<nesting level reached>" - else { - val showSelf = if (cl.selfType.isInstanceOf[EmptyType]) "" else " self: " + showNestedType(cl.selfType) + " =>" - - cl.structure.parents.map(showNestedType).mkString("", " with ", " {") + showSelf + - lines(truncateDecls(cl.structure.inherited).map(d => "^inherited^ " + showNestedDefinition(d))) + - lines(truncateDecls(cl.structure.declared).map(showNestedDefinition)) + - "}" - } - - def showType(t: Type)(implicit nesting: Int): String = t match { - case st: Projection => showType(st.prefix) + "#" + st.id - case st: ParameterRef => "<" + st.id + ">" - case st: Singleton => showPath(st.path) - case st: EmptyType => "<empty>" - case p: Parameterized => showType(p.baseType) + p.typeArguments.map(showType).mkString("[", ", ", "]") - case c: Constant => showType(c.baseType) + "(" + c.value + ")" - case a: Annotated => showAnnotations(a.annotations) + " " + showType(a.baseType) - case s: Structure => - s.parents.map(showType).mkString(" with ") + ( - if (nesting <= 0) "{ <nesting level reached> }" - else truncateDecls(s.declared).map(showNestedDefinition).mkString(" {", "\n", "}")) - case e: Existential => - showType(e.baseType) + ( - if (nesting <= 0) " forSome { <nesting level reached> }" - else e.clause.map(t => "type " + showNestedTypeParameter(t)).mkString(" forSome { ", "; ", " }")) - case p: Polymorphic => showType(p.baseType) + ( - if (nesting <= 0) " [ <nesting level reached> ]" - else showNestedTypeParameters(p.parameters)) - } - - private def showPath(p: Path): String = p.components.map(showPathComponent).mkString(".") - private def showPathComponent(pc: PathComponent) = pc match { - case s: Super => "super[" + showPath(s.qualifier) + "]" - case _: This => "this" - case i: Id => i.id - } - - private def space(s: String) = if (s.isEmpty) s else s + " " - private def showMonoDef(d: Definition, label: String)(implicit nesting: Int): String = - space(showAnnotations(d.annotations)) + space(showAccess(d.access)) + space(showModifiers(d.modifiers)) + space(label) + d.name - - private def showPolyDef(d: ParameterizedDefinition, label: String)(implicit nesting: Int): String = - showMonoDef(d, label) + showTypeParameters(d.typeParameters) - - private def showTypeParameters(tps: Seq[TypeParameter])(implicit nesting: Int): String = - if (tps.isEmpty) "" - else tps.map(showTypeParameter).mkString("[", ", ", "]") - - private def showTypeParameter(tp: TypeParameter)(implicit nesting: Int): String = - showAnnotations(tp.annotations) + " " + showVariance(tp.variance) + tp.id + showTypeParameters(tp.typeParameters) + " " + showBounds(tp.lowerBound, tp.upperBound) - - private def showAnnotations(as: Seq[Annotation])(implicit nesting: Int) = as.map(showAnnotation).mkString(" ") - private def showAnnotation(a: Annotation)(implicit nesting: Int) = - "@" + showType(a.base) + ( - if (a.arguments.isEmpty) "" - else a.arguments.map(a => a.name + " = " + a.value).mkString("(", ", ", ")") - ) - - private def showBounds(lower: Type, upper: Type)(implicit nesting: Int): String = ">: " + showType(lower) + " <: " + showType(upper) - - private def showValueParams(ps: Seq[ParameterList])(implicit nesting: Int): String = - ps.map(pl => - pl.parameters.map(mp => - mp.name + ": " + showParameterModifier(showType(mp.tpe), mp.modifier) + (if (mp.hasDefault) "= ..." else "") - ).mkString(if (pl.isImplicit) "(implicit " else "(", ", ", ")") - ).mkString("") - - private def showParameterModifier(base: String, pm: ParameterModifier): String = pm match { - case ParameterModifier.Plain => base - case ParameterModifier.Repeated => base + "*" - case ParameterModifier.ByName => "=> " + base - } - - private def showDefinitionType(d: DefinitionType) = d match { - case DefinitionType.Trait => "trait" - case DefinitionType.ClassDef => "class" - case DefinitionType.Module => "object" - case DefinitionType.PackageModule => "package object" - } - - private def showAccess(a: Access) = a match { - case p: Public => "" - case p: Protected => "protected" + showQualifier(p.qualifier) - case p: Private => "private" + showQualifier(p.qualifier) - } - - private def showQualifier(q: Qualifier) = q match { - case _: Unqualified => "" - case _: ThisQualifier => "[this]" - case i: IdQualifier => "[" + i.value + "]" - } - - private def showModifiers(m: Modifiers) = List( - (m.isOverride, "override"), - (m.isFinal, "final"), - (m.isSealed, "sealed"), - (m.isImplicit, "implicit"), - (m.isAbstract, "abstract"), - (m.isLazy, "lazy") - ).collect { case (true, mod) => mod }.mkString(" ") - - private def showVariance(v: Variance) = v match { - case Variance.Invariant => "" - case Variance.Covariant => "+" - case Variance.Contravariant => "-" - } - - // limit nesting to prevent cycles and generally keep output from getting humongous - private def showNestedType(tp: Type)(implicit nesting: Int) = showType(tp)(nesting - 1) - private def showNestedTypeParameter(tp: TypeParameter)(implicit nesting: Int) = showTypeParameter(tp)(nesting - 1) - private def showNestedTypeParameters(tps: Seq[TypeParameter])(implicit nesting: Int) = showTypeParameters(tps)(nesting - 1) - private def showNestedDefinition(d: Definition)(implicit nesting: Int) = showDefinition(d)(nesting - 1) -} diff --git a/src/dotty/tools/dotc/sbt/ThunkHolder.scala b/src/dotty/tools/dotc/sbt/ThunkHolder.scala deleted file mode 100644 index e377de6da..000000000 --- a/src/dotty/tools/dotc/sbt/ThunkHolder.scala +++ /dev/null @@ -1,61 +0,0 @@ -package dotty.tools.dotc -package sbt - -import scala.annotation.tailrec -import scala.collection.mutable.ListBuffer -import xsbti.api - -/** Create and hold thunks. A thunk is a (potentially) unevaluated value - * that may be evaluated once. - */ -private[sbt] trait ThunkHolder { - private[this] val thunks = new ListBuffer[api.Lazy[_]] - - /** Force all unevaluated thunks to prevent space leaks. */ - @tailrec protected final def forceThunks(): Unit = if (!thunks.isEmpty) { - val toForce = thunks.toList - thunks.clear() - toForce.foreach(_.get()) - // Forcing thunks may create new thunks - forceThunks() - } - - /** Store the by-name parameter `s` in a `Lazy` container without evaluating it. - * It will be forced by the next call to `forceThunks()` - */ - def lzy[T <: AnyRef](t: => T): api.Lazy[T] = { - val l = SafeLazy(() => t) - thunks += l - l - } - - /** Store the parameter `s` in a `Lazy` container, since `s` is not by-name, there - * is nothing to force. - * - * TODO: Get rid of this method. It is only needed because some xsbti.api classes - * take lazy arguments when they could be strict, but this can be fixed in sbt, - * see https://github.com/sbt/zinc/issues/114 - */ - def strict2lzy[T <: AnyRef](t: T): api.Lazy[T] = - SafeLazy.strict(t) -} - -// TODO: Use xsbti.SafeLazy once https://github.com/sbt/zinc/issues/113 is fixed -private object SafeLazy { - def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] = - new Impl(eval) - - def strict[T <: AnyRef](value: T): xsbti.api.Lazy[T] = - new Strict(value) - - private[this] final class Impl[T <: AnyRef](private[this] var eval: () => T) extends xsbti.api.AbstractLazy[T] { - private[this] lazy val _t = { - val t = eval() - eval = null // clear the reference, ensuring the only memory we hold onto is the result - t - } - def get: T = _t - } - - private[this] final class Strict[T <: AnyRef](val get: T) extends xsbti.api.Lazy[T] with java.io.Serializable -} diff --git a/src/dotty/tools/dotc/transform/ArrayConstructors.scala b/src/dotty/tools/dotc/transform/ArrayConstructors.scala deleted file mode 100644 index 74213d332..000000000 --- a/src/dotty/tools/dotc/transform/ArrayConstructors.scala +++ /dev/null @@ -1,59 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TreeTransforms._ -import Contexts.Context -import Flags._ -import SymUtils._ -import Symbols._ -import SymDenotations._ -import Types._ -import Decorators._ -import DenotTransformers._ -import StdNames._ -import NameOps._ -import ast.Trees._ -import dotty.tools.dotc.ast.tpd -import util.Positions._ -import Names._ - -import collection.mutable -import ResolveSuper._ - -import scala.collection.immutable.:: - - -/** This phase rewrites calls to array constructors to newArray method in Dotty.runtime.Arrays module. - * - * It assummes that generic arrays have already been handled by typer(see Applications.convertNewGenericArray). - * Additionally it optimizes calls to scala.Array.ofDim functions by replacing them with calls to newArray with specific dimensions - */ -class ArrayConstructors extends MiniPhaseTransform { thisTransform => - import ast.tpd._ - - override def phaseName: String = "arrayConstructors" - - override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - def rewrite(elemType: Type, dims: List[Tree]) = - tpd.newArray(elemType, tree.tpe, tree.pos, JavaSeqLiteral(dims, TypeTree(defn.IntClass.typeRef))) - - if (tree.fun.symbol eq defn.ArrayConstructor) { - val TypeApply(tycon, targ :: Nil) = tree.fun - rewrite(targ.tpe, tree.args) - } else if ((tree.fun.symbol.maybeOwner eq defn.ArrayModule) && (tree.fun.symbol.name eq nme.ofDim) && !tree.tpe.isInstanceOf[MethodicType]) { - val Apply(Apply(TypeApply(_, List(tp)), _), _) = tree - val cs = tp.tpe.widen.classSymbol - tree.fun match { - case Apply(TypeApply(t: Ident, targ), dims) - if !TypeErasure.isUnboundedGeneric(targ.head.tpe) && !ValueClasses.isDerivedValueClass(cs) => - rewrite(targ.head.tpe, dims) - case Apply(TypeApply(t: Select, targ), dims) - if !TypeErasure.isUnboundedGeneric(targ.head.tpe) && !ValueClasses.isDerivedValueClass(cs) => - Block(t.qualifier :: Nil, rewrite(targ.head.tpe, dims)) - case _ => tree - } - - } else tree - } -} diff --git a/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala b/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala deleted file mode 100644 index 9c01aaa9a..000000000 --- a/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala +++ /dev/null @@ -1,101 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TreeTransforms._ -import Contexts.Context -import Flags._ -import SymUtils._ -import Symbols._ -import SymDenotations._ -import Types._ -import Decorators._ -import DenotTransformers._ -import Annotations._ -import StdNames._ -import NameOps._ -import ast.Trees._ - -/** This phase augments Scala2 traits with implementation classes and with additional members - * needed for mixin composition. - * These symbols would have been added between Unpickling and Mixin in the Scala2 pipeline. - * Specifcally, it adds - * - * - an implementation class which defines a trait constructor and trait method implementations - * - trait setters for vals defined in traits - * - * Furthermore, it expands the names of all private getters and setters as well as super accessors in the trait and makes - * them not-private. - */ -class AugmentScala2Traits extends MiniPhaseTransform with IdentityDenotTransformer with FullParameterization { thisTransform => - import ast.tpd._ - - override def phaseName: String = "augmentScala2Traits" - - override def rewiredTarget(referenced: Symbol, derived: Symbol)(implicit ctx: Context) = NoSymbol - - override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { - val cls = impl.symbol.owner.asClass - for (mixin <- cls.mixins) - if (mixin.is(Scala2x)) - augmentScala2Trait(mixin, cls) - impl - } - - private def augmentScala2Trait(mixin: ClassSymbol, cls: ClassSymbol)(implicit ctx: Context): Unit = { - if (mixin.implClass.is(Scala2x)) () // nothing to do, mixin was already augmented - else { - //println(i"creating new implclass for $mixin ${mixin.implClass}") - val ops = new MixinOps(cls, thisTransform) - import ops._ - - val implClass = ctx.newCompleteClassSymbol( - owner = mixin.owner, - name = mixin.name.implClassName, - flags = Abstract | Scala2x, - parents = defn.ObjectType :: Nil, - assocFile = mixin.assocFile).enteredAfter(thisTransform) - - def implMethod(meth: TermSymbol): Symbol = { - val mold = - if (meth.isConstructor) - meth.copySymDenotation( - name = nme.TRAIT_CONSTRUCTOR, - info = MethodType(Nil, defn.UnitType)) - else meth.ensureNotPrivate - meth.copy( - owner = implClass, - name = mold.name.asTermName, - flags = Method | JavaStatic | mold.flags & ExpandedName, - info = fullyParameterizedType(mold.info, mixin)) - } - - def traitSetter(getter: TermSymbol) = - getter.copy( - name = getter.ensureNotPrivate.name - .expandedName(getter.owner, nme.TRAIT_SETTER_SEPARATOR) - .asTermName.setterName, - flags = Method | Accessor | ExpandedName, - info = MethodType(getter.info.resultType :: Nil, defn.UnitType)) - - for (sym <- mixin.info.decls) { - if (needsForwarder(sym) || sym.isConstructor || sym.isGetter && sym.is(Lazy) || sym.is(Method, butNot = Deferred)) - implClass.enter(implMethod(sym.asTerm)) - if (sym.isGetter) - if (sym.is(Lazy)) { - if (!sym.hasAnnotation(defn.VolatileAnnot)) - sym.addAnnotation(Annotation(defn.VolatileAnnot, Nil)) - } - else if (!sym.is(Deferred) && !sym.setter.exists && - !sym.info.resultType.isInstanceOf[ConstantType]) - traitSetter(sym.asTerm).enteredAfter(thisTransform) - if ((sym.is(PrivateAccessor, butNot = ExpandedName) && - (sym.isGetter || sym.isSetter)) // strangely, Scala 2 fields are also methods that have Accessor set. - || sym.is(SuperAccessor)) // scala2 superaccessors are pickled as private, but are compiled as public expanded - sym.ensureNotPrivate.installAfter(thisTransform) - } - ctx.log(i"Scala2x trait decls of $mixin = ${mixin.info.decls.toList.map(_.showDcl)}%\n %") - ctx.log(i"Scala2x impl decls of $mixin = ${implClass.info.decls.toList.map(_.showDcl)}%\n %") - } - } -} diff --git a/src/dotty/tools/dotc/transform/CapturedVars.scala b/src/dotty/tools/dotc/transform/CapturedVars.scala deleted file mode 100644 index cd05589c3..000000000 --- a/src/dotty/tools/dotc/transform/CapturedVars.scala +++ /dev/null @@ -1,149 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import core.SymDenotations._ -import core.StdNames.nme -import core.Names._ -import core.NameOps._ -import ast.Trees._ -import SymUtils._ -import collection.{ mutable, immutable } -import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet } - -class CapturedVars extends MiniPhase with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - /** the following two members override abstract members in Transform */ - val phaseName: String = "capturedVars" - val treeTransform = new Transform(Set()) - - private class RefInfo(implicit ctx: Context) { - /** The classes for which a Ref type exists. */ - val refClassKeys: collection.Set[Symbol] = - defn.ScalaNumericValueClasses() + defn.BooleanClass + defn.ObjectClass - - val refClass: Map[Symbol, Symbol] = - refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap - - val volatileRefClass: Map[Symbol, Symbol] = - refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap - - val boxedRefClasses: collection.Set[Symbol] = - refClassKeys.flatMap(k => Set(refClass(k), volatileRefClass(k))) - } - - class Transform(captured: collection.Set[Symbol]) extends TreeTransform { - def phase = thisTransform - - private var myRefInfo: RefInfo = null - private def refInfo(implicit ctx: Context) = { - if (myRefInfo == null) myRefInfo = new RefInfo() - myRefInfo - } - - private class CollectCaptured(implicit ctx: Context) extends EnclosingMethodTraverser { - private val captured = mutable.HashSet[Symbol]() - def traverse(enclMeth: Symbol, tree: Tree)(implicit ctx: Context) = tree match { - case id: Ident => - val sym = id.symbol - if (sym.is(Mutable, butNot = Method) && sym.owner.isTerm && sym.enclosingMethod != enclMeth) { - ctx.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth") - captured += sym - } - case _ => - foldOver(enclMeth, tree) - } - def runOver(tree: Tree): collection.Set[Symbol] = { - apply(NoSymbol, tree) - captured - } - } - - override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { - val captured = (new CollectCaptured)(ctx.withPhase(thisTransform)) - .runOver(ctx.compilationUnit.tpdTree) - new Transform(captured) - } - - /** The {Volatile|}{Int|Double|...|Object}Ref class corresponding to the class `cls`, - * depending on whether the reference should be @volatile - */ - def refClass(cls: Symbol, isVolatile: Boolean)(implicit ctx: Context): Symbol = { - val refMap = if (isVolatile) refInfo.volatileRefClass else refInfo.refClass - if (cls.isClass) { - refMap.getOrElse(cls, refMap(defn.ObjectClass)) - } - else refMap(defn.ObjectClass) - } - - override def prepareForValDef(vdef: ValDef)(implicit ctx: Context) = { - val sym = vdef.symbol - if (captured contains sym) { - val newd = sym.denot(ctx.withPhase(thisTransform)).copySymDenotation( - info = refClass(sym.info.classSymbol, sym.hasAnnotation(defn.VolatileAnnot)).typeRef, - initFlags = sym.flags &~ Mutable) - newd.removeAnnotation(defn.VolatileAnnot) - newd.installAfter(thisTransform) - } - this - } - - override def transformValDef(vdef: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - val vble = vdef.symbol - if (captured contains vble) { - def boxMethod(name: TermName): Tree = - ref(vble.info.classSymbol.companionModule.info.member(name).symbol) - cpy.ValDef(vdef)( - rhs = vdef.rhs match { - case EmptyTree => boxMethod(nme.zero).appliedToNone.withPos(vdef.pos) - case arg => boxMethod(nme.create).appliedTo(arg) - }, - tpt = TypeTree(vble.info).withPos(vdef.tpt.pos)) - } else vdef - } - - override def transformIdent(id: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = { - val vble = id.symbol - if (captured(vble)) - (id select nme.elem).ensureConforms(vble.denot(ctx.withPhase(thisTransform)).info) - else id - } - - /** If assignment is to a boxed ref type, e.g. - * - * intRef.elem = expr - * - * rewrite using a temporary var to - * - * val ev$n = expr - * intRef.elem = ev$n - * - * That way, we avoid the problem that `expr` might contain a `try` that would - * run on a non-empty stack (which is illegal under JVM rules). Note that LiftTry - * has already run before, so such `try`s would not be eliminated. - * - * Also: If the ref type lhs is followed by a cast (can be an artifact of nested translation), - * drop the cast. - */ - override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = { - def recur(lhs: Tree): Tree = lhs match { - case TypeApply(Select(qual, nme.asInstanceOf_), _) => - val Select(_, nme.elem) = qual - recur(qual) - case Select(_, nme.elem) if refInfo.boxedRefClasses.contains(lhs.symbol.maybeOwner) => - val tempDef = transformFollowing(SyntheticValDef(ctx.freshName("ev$").toTermName, tree.rhs)) - transformFollowing(Block(tempDef :: Nil, cpy.Assign(tree)(lhs, ref(tempDef.symbol)))) - case _ => - tree - } - recur(tree.lhs) - } - } -} diff --git a/src/dotty/tools/dotc/transform/CheckReentrant.scala b/src/dotty/tools/dotc/transform/CheckReentrant.scala deleted file mode 100644 index c9eefb22f..000000000 --- a/src/dotty/tools/dotc/transform/CheckReentrant.scala +++ /dev/null @@ -1,95 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Names._ -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform, TreeTransformer} -import ast.Trees._ -import Flags._ -import Types._ -import Constants.Constant -import Contexts.Context -import Symbols._ -import SymDenotations._ -import Decorators._ -import dotty.tools.dotc.core.Annotations.ConcreteAnnotation -import dotty.tools.dotc.core.Denotations.SingleDenotation -import scala.collection.mutable -import DenotTransformers._ -import typer.Checking -import Names.Name -import NameOps._ -import StdNames._ - - -/** A no-op transform that checks whether the compiled sources are re-entrant. - * If -Ycheck:reentrant is set, the phase makes sure that there are no variables - * that are accessible from a global object. It excludes from checking paths that - * are labeled with one of the annotations - * - * @sharable Indicating a class or val can be safely shared - * @unshared Indicating an object will not be accessed from multiple threads - * - * Currently the analysis is only intended to check the dotty compiler itself. To make - * it generally useful we'd need to add at least the following: - * - * - Handle polymorphic instantiation: We might instantiate a generic class - * with a type that contains vars. If the class contains fields of the generic - * type, this may constitute a path to a shared var, which currently goes undetected. - * - Handle arrays: Array elements are currently ignored because they are often used - * in an immutable way anyway. To do better, it would be helpful to have a type - * for immutable array. - */ -class CheckReentrant extends MiniPhaseTransform { thisTransformer => - import ast.tpd._ - - override def phaseName = "checkReentrant" - - private var shared: Set[Symbol] = Set() - private var seen: Set[ClassSymbol] = Set() - private var indent: Int = 0 - - private val sharableAnnot = new CtxLazy(implicit ctx => - ctx.requiredClass("dotty.tools.sharable")) - private val unsharedAnnot = new CtxLazy(implicit ctx => - ctx.requiredClass("dotty.tools.unshared")) - - def isIgnored(sym: Symbol)(implicit ctx: Context) = - sym.hasAnnotation(sharableAnnot()) || - sym.hasAnnotation(unsharedAnnot()) - - def scanning(sym: Symbol)(op: => Unit)(implicit ctx: Context): Unit = { - ctx.log(i"${" " * indent}scanning $sym") - indent += 1 - try op - finally indent -= 1 - } - - def addVars(cls: ClassSymbol)(implicit ctx: Context): Unit = { - if (!seen.contains(cls) && !isIgnored(cls)) { - seen += cls - scanning(cls) { - for (sym <- cls.classInfo.decls) - if (sym.isTerm && !sym.isSetter && !isIgnored(sym)) - if (sym.is(Mutable)) { - ctx.error( - i"""possible data race involving globally reachable ${sym.showLocated}: ${sym.info} - | use -Ylog:checkReentrant+ to find out more about why the variable is reachable.""") - shared += sym - } else if (!sym.is(Method) || sym.is(Accessor | ParamAccessor)) { - scanning(sym) { - sym.info.widenExpr.classSymbols.foreach(addVars) - } - } - for (parent <- cls.classInfo.classParents) - addVars(parent.symbol.asClass) - } - } - } - - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (ctx.settings.YcheckReentrant.value && tree.symbol.owner.isStaticOwner) - addVars(tree.symbol.owner.asClass) - tree - } -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/CheckStatic.scala b/src/dotty/tools/dotc/transform/CheckStatic.scala deleted file mode 100644 index 937a4f1cc..000000000 --- a/src/dotty/tools/dotc/transform/CheckStatic.scala +++ /dev/null @@ -1,96 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Names._ -import StdNames.nme -import Types._ -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform, TreeTransformer} -import ast.Trees._ -import Flags._ -import Contexts.Context -import Symbols._ -import Constants._ -import Denotations._, SymDenotations._ -import Decorators.StringInterpolators -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Annotations.ConcreteAnnotation -import scala.collection.mutable -import DenotTransformers._ -import Names.Name -import NameOps._ -import Decorators._ -import TypeUtils._ - -/** A transformer that check that requirements of Static fields\methods are implemented: - * 1. Only objects can have members annotated with `@static` - * 2. The fields annotated with `@static` should preceed any non-`@static` fields. - * This ensures that we do not introduce surprises for users in initialization order. - * 3. If a member `foo` of an `object C` is annotated with `@static`, - * the companion class `C` is not allowed to define term members with name `foo`. - * 4. If a member `foo` of an `object C` is annotated with `@static`, the companion class `C` - * is not allowed to inherit classes that define a term member with name `foo`. - * 5. Only `@static` methods and vals are supported in companions of traits. - * Java8 supports those, but not vars, and JavaScript does not have interfaces at all. - * 6. `@static` Lazy vals are currently unsupported. - */ -class CheckStatic extends MiniPhaseTransform { thisTransformer => - import ast.tpd._ - - override def phaseName = "checkStatic" - - - def check(tree: tpd.DefTree)(implicit ctx: Context) = { - - } - - override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - val defns = tree.body.collect{case t: ValOrDefDef => t} - var hadNonStaticField = false - for(defn <- defns) { - if (defn.symbol.hasAnnotation(ctx.definitions.ScalaStaticAnnot)) { - if(!ctx.owner.is(Module)) { - ctx.error("@static fields are only allowed inside objects", defn.pos) - } - - if (defn.isInstanceOf[ValDef] && hadNonStaticField) { - ctx.error("@static fields should preceed non-static ones", defn.pos) - } - - val companion = ctx.owner.companionClass - def clashes = companion.asClass.membersNamed(defn.name) - - if (!companion.exists) { - ctx.error("object that contains @static members should have companion class", defn.pos) - } else if (clashes.exists) { - ctx.error("companion classes cannot define members with same name as @static member", defn.pos) - } else if (defn.symbol.is(Flags.Mutable) && companion.is(Flags.Trait)) { - ctx.error("Companions of traits cannot define mutable @static fields", defn.pos) - } else if (defn.symbol.is(Flags.Lazy)) { - ctx.error("Lazy @static fields are not supported", defn.pos) - } else if (defn.symbol.allOverriddenSymbols.nonEmpty) { - ctx.error("@static members cannot override or implement non-static ones", defn.pos) - } - } else hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef] - - } - tree - } - - override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - if (tree.symbol.hasAnnotation(defn.ScalaStaticAnnot)) { - val symbolWhitelist = tree.symbol.ownersIterator.flatMap(x => if (x.is(Flags.Module)) List(x, x.companionModule) else List(x)).toSet - def isSafeQual(t: Tree): Boolean = { // follow the desugared paths created by typer - t match { - case t: This => true - case t: Select => isSafeQual(t.qualifier) && symbolWhitelist.contains(t.symbol) - case t: Ident => symbolWhitelist.contains(t.symbol) - case t: Block => t.stats.forall(tpd.isPureExpr) && isSafeQual(t.expr) - } - } - if (isSafeQual(tree.qualifier)) - ref(tree.symbol) - else tree - } else tree - } -} diff --git a/src/dotty/tools/dotc/transform/ClassOf.scala b/src/dotty/tools/dotc/transform/ClassOf.scala deleted file mode 100644 index e7b6977c7..000000000 --- a/src/dotty/tools/dotc/transform/ClassOf.scala +++ /dev/null @@ -1,30 +0,0 @@ -package dotty.tools.dotc -package transform - -import ast.tpd -import core.Constants.Constant -import core.Contexts.Context -import core.StdNames.nme -import core.Symbols.{defn,TermSymbol} -import core.TypeErasure -import TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform} - -/** Rewrite `classOf` calls as follow: - * - * For every primitive class C whose boxed class is called B: - * classOf[C] -> B.TYPE - * For every non-primitive class D: - * classOf[D] -> Literal(Constant(erasure(D))) - */ -class ClassOf extends MiniPhaseTransform { - import tpd._ - - override def phaseName: String = "classOf" - - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = - if (tree.symbol eq defn.Predef_classOf) { - val targ = tree.args.head.tpe - clsOf(targ).ensureConforms(tree.tpe).withPos(tree.pos) - } - else tree -} diff --git a/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/src/dotty/tools/dotc/transform/CollectEntryPoints.scala deleted file mode 100644 index 714255962..000000000 --- a/src/dotty/tools/dotc/transform/CollectEntryPoints.scala +++ /dev/null @@ -1,116 +0,0 @@ -package dotty.tools.dotc.transform - -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer, MiniPhaseTransform} -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.Context -import scala.collection.mutable.ListBuffer -import dotty.tools.dotc.core.{Scopes, Flags} -import dotty.tools.dotc.core.Symbols.NoSymbol -import scala.annotation.tailrec -import dotty.tools.dotc.core._ -import Symbols._ -import scala.Some -import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer} -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.Context -import scala.collection.mutable -import dotty.tools.dotc.core.Names.Name -import NameOps._ -import Types._ -import scala.collection.SortedSet -import Decorators._ -import StdNames._ -import dotty.tools.dotc.util.Positions.Position -import dotty.tools.dotc.config.JavaPlatform - -class CollectEntryPoints extends MiniPhaseTransform { - - /** perform context-dependant initialization */ - override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context) = { - entryPoints = collection.immutable.TreeSet.empty[Symbol](new SymbolOrdering()) - assert(ctx.platform.isInstanceOf[JavaPlatform], "Java platform specific phase") - this - } - - private var entryPoints: Set[Symbol] = _ - - def getEntryPoints = entryPoints.toList - - override def phaseName: String = "collectEntryPoints" - override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - if (tree.symbol.owner.isClass && isJavaEntryPoint(tree.symbol)) { - // collecting symbols for entry points here (as opposed to GenBCode where they are used) - // has the advantage of saving an additional pass over all ClassDefs. - entryPoints += tree.symbol - } - tree - } - - def isJavaEntryPoint(sym: Symbol)(implicit ctx: Context): Boolean = { - def fail(msg: String, pos: Position = sym.pos) = { - ctx.warning(sym.name + - s" has a main method with parameter type Array[String], but ${sym.fullName} will not be a runnable program.\n Reason: $msg", - sourcePos(sym.pos) - // TODO: make this next claim true, if possible - // by generating valid main methods as static in module classes - // not sure what the jvm allows here - // + " You can still run the program by calling it as " + javaName(sym) + " instead." - ) - false - } - def failNoForwarder(msg: String) = { - fail(s"$msg, which means no static forwarder can be generated.\n") - } - val possibles = if (sym.flags is Flags.Module) (sym.info nonPrivateMember nme.main).alternatives else Nil - val hasApproximate = possibles exists { - m => - m.info match { - case MethodType(_, p :: Nil) => - p.typeSymbol == defn.ArrayClass - case _ => false - } - } - def precise(implicit ctx: Context) = { - val companion = sym.companionClass //sym.asClass.linkedClassOfClass - val javaPlatform = ctx.platform.asInstanceOf[JavaPlatform] - if (javaPlatform.hasJavaMainMethod(companion)) - failNoForwarder("companion contains its own main method") - else if (companion.exists && companion.info.member(nme.main).exists) - // this is only because forwarders aren't smart enough yet - failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") - else if (companion.flags is Flags.Trait) - failNoForwarder("companion is a trait") - // Now either succeed, or issue some additional warnings for things which look like - // attempts to be java main methods. - else (possibles exists (x => javaPlatform.isJavaMainMethod(x.symbol))) || { - possibles exists { - m => - m.symbol.info match { - case t: PolyType => - fail("main methods cannot be generic.") - case t@MethodType(paramNames, paramTypes) => - if (t.resultType :: paramTypes exists (_.typeSymbol.isAbstractType)) - fail("main methods cannot refer to type parameters or abstract types.", m.symbol.pos) - else - javaPlatform.isJavaMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos) - case tp => - fail(s"don't know what this is: $tp", m.symbol.pos) - } - } - } - } - - // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. - hasApproximate && precise(ctx.withPhase(ctx.erasurePhase)) - // Before erasure so we can identify generic mains. - - -} - -} - -class SymbolOrdering(implicit ctx: Context) extends Ordering[Symbol] { - override def compare(x: Symbol, y: Symbol): Int = { - x.fullName.toString.compareTo(y.fullName.toString) - } -} diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala deleted file mode 100644 index db850e944..000000000 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ /dev/null @@ -1,261 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TreeTransforms._ -import dotty.tools.dotc.ast.tpd._ -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.StdNames._ -import Phases._ -import ast._ -import Trees._ -import Flags._ -import SymUtils._ -import Symbols._ -import SymDenotations._ -import Types._ -import Decorators._ -import DenotTransformers._ -import util.Positions._ -import Constants.Constant -import collection.mutable - -/** This transform - * - moves initializers from body to constructor. - * - makes all supercalls explicit - * - also moves private fields that are accessed only from constructor - * into the constructor if possible. - */ -class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import tpd._ - - override def phaseName: String = "constructors" - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Memoize]) - - - // Collect all private parameter accessors and value definitions that need - // to be retained. There are several reasons why a parameter accessor or - // definition might need to be retained: - // 1. It is accessed after the constructor has finished - // 2. It is accessed before it is defined - // 3. It is accessed on an object other than `this` - // 4. It is a mutable parameter accessor - // 5. It is has a wildcard initializer `_` - private val retainedPrivateVals = mutable.Set[Symbol]() - private val seenPrivateVals = mutable.Set[Symbol]() - - private def markUsedPrivateSymbols(tree: RefTree)(implicit ctx: Context): Unit = { - - val sym = tree.symbol - def retain() = - retainedPrivateVals.add(sym) - - if (sym.exists && sym.owner.isClass && mightBeDropped(sym)) { - val owner = sym.owner.asClass - - tree match { - case Ident(_) | Select(This(_), _) => - def inConstructor = { - val method = ctx.owner.enclosingMethod - method.isPrimaryConstructor && ctx.owner.enclosingClass == owner - } - if (inConstructor && (sym.is(ParamAccessor) || seenPrivateVals.contains(sym))) { - // used inside constructor, accessed on this, - // could use constructor argument instead, no need to retain field - } - else retain() - case _ => retain() - } - } - } - - override def transformIdent(tree: tpd.Ident)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - markUsedPrivateSymbols(tree) - tree - } - - override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - markUsedPrivateSymbols(tree) - tree - } - - override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - if (mightBeDropped(tree.symbol)) - (if (isWildcardStarArg(tree.rhs)) retainedPrivateVals else seenPrivateVals) += tree.symbol - tree - } - - /** All initializers for non-lazy fields should be moved into constructor. - * All non-abstract methods should be implemented (this is assured for constructors - * in this phase and for other methods in memoize). - */ - override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = { - tree match { - case tree: ValDef if tree.symbol.exists && tree.symbol.owner.isClass && !tree.symbol.is(Lazy) && !tree.symbol.hasAnnotation(defn.ScalaStaticAnnot) => - assert(tree.rhs.isEmpty, i"$tree: initializer should be moved to constructors") - case tree: DefDef if !tree.symbol.is(LazyOrDeferred) => - assert(!tree.rhs.isEmpty, i"unimplemented: $tree") - case _ => - } - } - - /** @return true if after ExplicitOuter, all references from this tree go via an - * outer link, so no parameter accessors need to be rewired to parameters - */ - private def noDirectRefsFrom(tree: Tree)(implicit ctx: Context) = - tree.isDef && tree.symbol.isClass && !tree.symbol.is(InSuperCall) - - /** Class members that can be eliminated if referenced only from their own - * constructor. - */ - private def mightBeDropped(sym: Symbol)(implicit ctx: Context) = - sym.is(Private, butNot = MethodOrLazy) && !sym.is(MutableParamAccessor) - - private final val MutableParamAccessor = allOf(Mutable, ParamAccessor) - - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { - val cls = ctx.owner.asClass - - val constr @ DefDef(nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr - - // Produce aligned accessors and constructor parameters. We have to adjust - // for any outer parameters, which are last in the sequence of original - // parameter accessors but come first in the constructor parameter list. - val accessors = cls.paramAccessors.filterNot(_.isSetter) - val vparamsWithOuterLast = vparams match { - case vparam :: rest if vparam.name == nme.OUTER => rest ::: vparam :: Nil - case _ => vparams - } - val paramSyms = vparamsWithOuterLast map (_.symbol) - - // Adjustments performed when moving code into the constructor: - // (1) Replace references to param accessors by constructor parameters - // except possibly references to mutable variables, if `excluded = Mutable`. - // (Mutable parameters should be replaced only during the super call) - // (2) If the parameter accessor reference was to an alias getter, - // drop the () when replacing by the parameter. - object intoConstr extends TreeMap { - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Ident(_) | Select(This(_), _) => - var sym = tree.symbol - if (sym is (ParamAccessor, butNot = Mutable)) sym = sym.subst(accessors, paramSyms) - if (sym.owner.isConstructor) ref(sym).withPos(tree.pos) else tree - case Apply(fn, Nil) => - val fn1 = transform(fn) - if ((fn1 ne fn) && fn1.symbol.is(Param) && fn1.symbol.owner.isPrimaryConstructor) - fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass - else cpy.Apply(tree)(fn1, Nil) - case _ => - if (noDirectRefsFrom(tree)) tree else super.transform(tree) - } - - def apply(tree: Tree, prevOwner: Symbol)(implicit ctx: Context): Tree = { - transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisTransform) - } - } - - def isRetained(acc: Symbol) = { - !mightBeDropped(acc) || retainedPrivateVals(acc) - } - - val constrStats, clsStats = new mutable.ListBuffer[Tree] - - /** Map outer getters $outer and outer accessors $A$B$$$outer to the given outer parameter. */ - def mapOuter(outerParam: Symbol) = new TreeMap { - override def transform(tree: Tree)(implicit ctx: Context) = tree match { - case Apply(fn, Nil) - if (fn.symbol.is(OuterAccessor) - || fn.symbol.isGetter && fn.symbol.name == nme.OUTER - ) && - fn.symbol.info.resultType.classSymbol == outerParam.info.classSymbol => - ref(outerParam) - case _ => - super.transform(tree) - } - } - - val dropped = mutable.Set[Symbol]() - - // Split class body into statements that go into constructor and - // definitions that are kept as members of the class. - def splitStats(stats: List[Tree]): Unit = stats match { - case stat :: stats1 => - stat match { - case stat @ ValDef(name, tpt, _) if !stat.symbol.is(Lazy) && !stat.symbol.hasAnnotation(defn.ScalaStaticAnnot) => - val sym = stat.symbol - if (isRetained(sym)) { - if (!stat.rhs.isEmpty && !isWildcardArg(stat.rhs)) - constrStats += Assign(ref(sym), intoConstr(stat.rhs, sym)).withPos(stat.pos) - clsStats += cpy.ValDef(stat)(rhs = EmptyTree) - } - else if (!stat.rhs.isEmpty) { - dropped += sym - sym.copySymDenotation( - initFlags = sym.flags &~ Private, - owner = constr.symbol).installAfter(thisTransform) - constrStats += intoConstr(stat, sym) - } - case DefDef(nme.CONSTRUCTOR, _, ((outerParam @ ValDef(nme.OUTER, _, _)) :: _) :: Nil, _, _) => - clsStats += mapOuter(outerParam.symbol).transform(stat) - case _: DefTree => - clsStats += stat - case _ => - constrStats += intoConstr(stat, tree.symbol) - } - splitStats(stats1) - case Nil => - (Nil, Nil) - } - splitStats(tree.body) - - // The initializers for the retained accessors */ - val copyParams = accessors flatMap { acc => - if (!isRetained(acc)) { - dropped += acc - Nil - } else { - val target = if (acc.is(Method)) acc.field else acc - if (!target.exists) Nil // this case arises when the parameter accessor is an alias - else { - val param = acc.subst(accessors, paramSyms) - val assigns = Assign(ref(target), ref(param)).withPos(tree.pos) :: Nil - if (acc.name != nme.OUTER) assigns - else { - // insert test: if ($outer eq null) throw new NullPointerException - val nullTest = - If(ref(param).select(defn.Object_eq).appliedTo(Literal(Constant(null))), - Throw(New(defn.NullPointerExceptionClass.typeRef, Nil)), - unitLiteral) - nullTest :: assigns - } - } - } - } - - // Drop accessors that are not retained from class scope - if (dropped.nonEmpty) { - val clsInfo = cls.classInfo - cls.copy( - info = clsInfo.derivedClassInfo( - decls = clsInfo.decls.filteredScope(!dropped.contains(_)))) - - // TODO: this happens to work only because Constructors is the last phase in group - } - - val (superCalls, followConstrStats) = constrStats.toList match { - case (sc: Apply) :: rest if sc.symbol.isConstructor => (sc :: Nil, rest) - case stats => (Nil, stats) - } - - val mappedSuperCalls = vparams match { - case (outerParam @ ValDef(nme.OUTER, _, _)) :: _ => - superCalls.map(mapOuter(outerParam.symbol).transform) - case _ => superCalls - } - - cpy.Template(tree)( - constr = cpy.DefDef(constr)( - rhs = Block(copyParams ::: mappedSuperCalls ::: followConstrStats, unitLiteral)), - body = clsStats.toList) - } -} diff --git a/src/dotty/tools/dotc/transform/CrossCastAnd.scala b/src/dotty/tools/dotc/transform/CrossCastAnd.scala deleted file mode 100644 index 4fc4ef10b..000000000 --- a/src/dotty/tools/dotc/transform/CrossCastAnd.scala +++ /dev/null @@ -1,30 +0,0 @@ -package dotty.tools.dotc.transform - -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Flags -import dotty.tools.dotc.core.Types.{NoType, Type, AndType} -import dotty.tools.dotc.transform.TreeTransforms._ -import tpd._ - -import scala.collection.mutable.ListBuffer - - -/** - * This transform makes sure that all private member selections from - * AndTypes are performed from the first component of AndType. - * This is needed for correctness of erasure. See `tests/run/PrivateAnd.scala` - */ -class CrossCastAnd extends MiniPhaseTransform { thisTransform => - - override def phaseName: String = "crossCast" - - override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - - lazy val qtype = tree.qualifier.tpe.widen - val sym = tree.symbol - if (sym.is(Flags.Private) && qtype.typeSymbol != sym.owner) - cpy.Select(tree)(tree.qualifier.asInstance(AndType(qtype.baseTypeWithArgs(sym.owner), tree.qualifier.tpe)), tree.name) - else tree - } -} diff --git a/src/dotty/tools/dotc/transform/CtxLazy.scala b/src/dotty/tools/dotc/transform/CtxLazy.scala deleted file mode 100644 index 7b317abef..000000000 --- a/src/dotty/tools/dotc/transform/CtxLazy.scala +++ /dev/null @@ -1,23 +0,0 @@ -package dotty.tools.dotc -package transform -import core.Contexts.Context - -/** Utility class for lazy values whose evaluation depends on a context. - * This should be used whenever the evaluation of a lazy expression - * depends on some context, but the value can be re-used afterwards - * with a different context. - * - * A typical use case is a lazy val in a phase object which exists once per root context where - * the expression intiializing the lazy val depends only on the root context, but not any changes afterwards. - */ -class CtxLazy[T](expr: Context => T) { - private var myValue: T = _ - private var forced = false - def apply()(implicit ctx: Context): T = { - if (!forced) { - myValue = expr(ctx) - forced = true - } - myValue - } -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/DropEmptyCompanions.scala.disabled b/src/dotty/tools/dotc/transform/DropEmptyCompanions.scala.disabled deleted file mode 100644 index 7b37c5881..000000000 --- a/src/dotty/tools/dotc/transform/DropEmptyCompanions.scala.disabled +++ /dev/null @@ -1,98 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.SymTransformer -import Phases.Phase -import Contexts.Context -import Flags._ -import Symbols._ -import SymDenotations.SymDenotation -import ast.Trees._ -import collection.mutable -import Decorators._ -import NameOps._ -import TreeTransforms.MiniPhaseTransform -import dotty.tools.dotc.transform.TreeTransforms.TransformerInfo - -/** Remove companion objects that are empty - * Lots of constraints here: - * 1. It's impractical to place DropEmptyCompanions before lambda lift because dropped - * modules can be anywhere and have hard to trace references. - * 2. DropEmptyCompanions cannot be interleaved with LambdaLift or Flatten because - * they put things in liftedDefs sets which cause them to surface later. So - * removed modules resurface. - * 3. DropEmptyCompanions has to be before RestoreScopes. - * The solution to the constraints is to put DropEmptyCompanions between Flatten - * and RestoreScopes and to only start working once we are back on PackageDef - * level, so we know that all objects moved by LambdaLift and Flatten have arrived - * at their destination. - */ -class DropEmptyCompanions extends MiniPhaseTransform { thisTransform => - import ast.tpd._ - override def phaseName = "dropEmptyCompanions" - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Flatten]) - - override def transformPackageDef(pdef: PackageDef)(implicit ctx: Context, info: TransformerInfo) = { - - /** Is `tree` an empty companion object? */ - def isEmptyCompanion(tree: Tree) = tree match { - case TypeDef(_, impl: Template) if tree.symbol.is(SyntheticModule) && - tree.symbol.companionClass.exists && - impl.body.forall(_.symbol.isPrimaryConstructor) => - ctx.log(i"removing ${tree.symbol}") - true - case _ => - false - } - - val dropped = pdef.stats.filter(isEmptyCompanion).map(_.symbol).toSet - - /** Symbol is a $lzy field representing a module */ - def isLazyModuleVar(sym: Symbol) = - sym.name.isLazyLocal && - sym.owner.info.decl(sym.name.asTermName.nonLazyName).symbol.is(Module) - - /** Symbol should be dropped together with a dropped companion object. - * Such symbols are: - * - lzy fields pointing to modules, - * - vals and getters representing modules. - */ - def symIsDropped(sym: Symbol): Boolean = - (sym.is(Module) || isLazyModuleVar(sym)) && - dropped.contains(sym.info.resultType.typeSymbol) - - /** Tree should be dropped because it (is associated with) an empty - * companion object. Such trees are - * - module classes of empty companion objects - * - definitions of lazy module variables or assignments to them. - * - vals and getters for empty companion objects - */ - def toDrop(stat: Tree): Boolean = stat match { - case stat: TypeDef => dropped.contains(stat.symbol) - case stat: ValOrDefDef => symIsDropped(stat.symbol) - case stat: Assign => symIsDropped(stat.lhs.symbol) - case _ => false - } - - def prune(tree: Tree): Tree = tree match { - case tree @ TypeDef(name, impl @ Template(constr, _, _, _)) => - cpy.TypeDef(tree)( - rhs = cpy.Template(impl)( - constr = cpy.DefDef(constr)(rhs = pruneLocals(constr.rhs)), - body = pruneStats(impl.body))) - case _ => - tree - } - - def pruneStats(stats: List[Tree]) = - stats.filterConserve(!toDrop(_)).mapConserve(prune) - - def pruneLocals(expr: Tree) = expr match { - case Block(stats, expr) => cpy.Block(expr)(pruneStats(stats), expr) - case _ => expr - } - - cpy.PackageDef(pdef)(pdef.pid, pruneStats(pdef.stats)) - } -} diff --git a/src/dotty/tools/dotc/transform/DropInlined.scala b/src/dotty/tools/dotc/transform/DropInlined.scala deleted file mode 100644 index 775663b5c..000000000 --- a/src/dotty/tools/dotc/transform/DropInlined.scala +++ /dev/null @@ -1,15 +0,0 @@ -package dotty.tools.dotc -package transform - -import typer.Inliner -import core.Contexts.Context -import TreeTransforms.{MiniPhaseTransform, TransformerInfo} - -/** Drop Inlined nodes */ -class DropInlined extends MiniPhaseTransform { - import ast.tpd._ - override def phaseName = "dropInlined" - - override def transformInlined(tree: Inlined)(implicit ctx: Context, info: TransformerInfo): Tree = - Inliner.dropInlined(tree) -} diff --git a/src/dotty/tools/dotc/transform/ElimByName.scala b/src/dotty/tools/dotc/transform/ElimByName.scala deleted file mode 100644 index 192227261..000000000 --- a/src/dotty/tools/dotc/transform/ElimByName.scala +++ /dev/null @@ -1,129 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core._ -import DenotTransformers._ -import Symbols._ -import SymDenotations._ -import Contexts._ -import Types._ -import Flags._ -import Decorators._ -import SymUtils._ -import util.Attachment -import core.StdNames.nme -import ast.Trees._ - -/** This phase eliminates ExprTypes `=> T` as types of function parameters, and replaces them by - * nullary function types. More precisely: - * - * For the types of parameter symbols: - * - * => T ==> () => T - * - * Note that `=> T` types are not eliminated in MethodTypes. This is done later at erasure. - * Terms are rewritten as follows: - * - * x ==> x.apply() if x is a parameter that had type => T - * - * Arguments to call-by-name parameters are translated as follows. First, the argument is - * rewritten by the rules - * - * e.apply() ==> e if e.apply() is an argument to a call-by-name parameter - * expr ==> () => expr if other expr is an argument to a call-by-name parameter - * - * This makes the argument compatible with a parameter type of () => T, which will be the - * formal parameter type at erasure. But to be -Ycheckable until then, any argument - * ARG rewritten by the rules above is again wrapped in an application DummyApply(ARG) - * where - * - * DummyApply: [T](() => T): T - * - * is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers. - * - * Note: This scheme to have inconsistent types between method types (whose formal types are still - * ExprTypes and parameter valdefs (which are now FunctionTypes) is not pretty. There are two - * other options which have been abandoned or not yet pursued. - * - * Option 1: Transform => T to () => T also in method and function types. The problem with this is - * that is that it requires to look at every type, and this forces too much, causing - * Cyclic Reference errors. Abandoned for this reason. - * - * Option 2: Merge ElimByName with erasure, or have it run immediately before. This has not been - * tried yet. - */ -class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransformer => - import ast.tpd._ - - override def phaseName: String = "elimByName" - - override def runsAfterGroupsOf = Set(classOf[Splitter]) - // assumes idents and selects have symbols; interferes with splitter distribution - // that's why it's "after group". - - /** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */ - private def originalDenotation(tree: Tree)(implicit ctx: Context) = - tree.symbol.denot(ctx.withPhase(thisTransformer)) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { - - def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match { - case formalExpr: ExprType => - val argType = arg.tpe.widen - val argFun = arg match { - case Apply(Select(qual, nme.apply), Nil) - if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) => - qual - case _ => - val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags - val meth = ctx.newSymbol( - ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType)) - Closure(meth, _ => arg.changeOwner(ctx.owner, meth)) - } - ref(defn.dummyApply).appliedToType(argType).appliedTo(argFun) - case _ => - arg - } - - val MethodType(_, formals) = tree.fun.tpe.widen - val args1 = tree.args.zipWithConserve(formals)(transformArg) - cpy.Apply(tree)(tree.fun, args1) - } - - /** If denotation had an ExprType before, it now gets a function type */ - private def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = - (symd is Param) || (symd is (ParamAccessor, butNot = Method)) - - /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */ - private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = { - val origDenot = originalDenotation(ftree) - if (exprBecomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType])) - tree.select(defn.Function0_apply).appliedToNone - else tree - } - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = - applyIfFunction(tree, tree) - - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { - case TypeApply(Select(_, nme.asInstanceOf_), arg :: Nil) => - // tree might be of form e.asInstanceOf[x.type] where x becomes a function. - // See pos/t296.scala - applyIfFunction(tree, arg) - case _ => tree - } - - override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = - if (exprBecomesFunction(tree.symbol)) - cpy.ValDef(tree)(tpt = tree.tpt.withType(tree.symbol.info)) - else tree - - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match { - case ExprType(rt) if exprBecomesFunction(sym) => defn.FunctionOf(Nil, rt) - case _ => tp - } - - override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isTerm -} diff --git a/src/dotty/tools/dotc/transform/ElimErasedValueType.scala b/src/dotty/tools/dotc/transform/ElimErasedValueType.scala deleted file mode 100644 index 24c8cdc8d..000000000 --- a/src/dotty/tools/dotc/transform/ElimErasedValueType.scala +++ /dev/null @@ -1,84 +0,0 @@ -package dotty.tools.dotc -package transform - -import ast.{Trees, tpd} -import core._, core.Decorators._ -import TreeTransforms._, Phases.Phase -import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ -import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ -import TypeErasure.ErasedValueType, ValueClasses._ - -/** This phase erases ErasedValueType to their underlying type. - * It also removes the synthetic cast methods u2evt$ and evt2u$ which are - * no longer needed afterwards. - */ -class ElimErasedValueType extends MiniPhaseTransform with InfoTransformer { - - import tpd._ - - override def phaseName: String = "elimErasedValueType" - - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) - - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = sym match { - case sym: ClassSymbol if sym is ModuleClass => - sym.companionClass match { - case origClass: ClassSymbol if isDerivedValueClass(origClass) => - val cinfo = tp.asInstanceOf[ClassInfo] - val decls1 = cinfo.decls.cloneScope - ctx.atPhase(this.next) { implicit ctx => - // Remove synthetic cast methods introduced by ExtensionMethods, - // they are no longer needed after this phase. - decls1.unlink(cinfo.decl(nme.U2EVT).symbol) - decls1.unlink(cinfo.decl(nme.EVT2U).symbol) - } - cinfo.derivedClassInfo(decls = decls1) - case _ => - tp - } - case _ => - elimEVT(tp) - } - - def elimEVT(tp: Type)(implicit ctx: Context): Type = tp match { - case ErasedValueType(_, underlying) => - elimEVT(underlying) - case tp: MethodType => - val paramTypes = tp.paramTypes.mapConserve(elimEVT) - val retType = elimEVT(tp.resultType) - tp.derivedMethodType(tp.paramNames, paramTypes, retType) - case _ => - tp - } - - def transformTypeOfTree(tree: Tree)(implicit ctx: Context): Tree = - tree.withType(elimEVT(tree.tpe)) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { - val Apply(fun, args) = tree - - // The casts to and from ErasedValueType are no longer needed once ErasedValueType - // has been eliminated. - val t = - if (fun.symbol.isValueClassConvertMethod) - args.head - else - tree - transformTypeOfTree(t) - } - - override def transformInlined(tree: Inlined)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) - - // FIXME: transformIf and transformBlock won't be required anymore once #444 is fixed. - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) - override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) - override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) - override def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) -} diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala deleted file mode 100644 index 258b7f234..000000000 --- a/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ /dev/null @@ -1,135 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Names._ -import StdNames.nme -import Types._ -import dotty.tools.dotc.transform.TreeTransforms.{AnnotationTransformer, TransformerInfo, MiniPhaseTransform, TreeTransformer} -import ast.Trees._ -import Flags._ -import Contexts.Context -import Symbols._ -import Constants._ -import Denotations._, SymDenotations._ -import Decorators.StringInterpolators -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Annotations.ConcreteAnnotation -import scala.collection.mutable -import DenotTransformers._ -import Names.Name -import NameOps._ -import TypeUtils._ - -/** A transformer that removes repeated parameters (T*) from all types, replacing - * them with Seq types. - */ -class ElimRepeated extends MiniPhaseTransform with InfoTransformer with AnnotationTransformer { thisTransformer => - import ast.tpd._ - - override def phaseName = "elimRepeated" - - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - elimRepeated(tp) - - override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym is Method - - private def elimRepeated(tp: Type)(implicit ctx: Context): Type = tp.stripTypeVar match { - case tp @ MethodType(paramNames, paramTypes) => - val resultType1 = elimRepeated(tp.resultType) - val paramTypes1 = - if (paramTypes.nonEmpty && paramTypes.last.isRepeatedParam) { - val last = paramTypes.last.underlyingIfRepeated(tp.isJava) - paramTypes.init :+ last - } else paramTypes - tp.derivedMethodType(paramNames, paramTypes1, resultType1) - case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimRepeated(tp.resultType)) - case tp => - tp - } - - def transformTypeOfTree(tree: Tree)(implicit ctx: Context): Tree = - tree.withType(elimRepeated(tree.tpe)) - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { - val args1 = tree.args.map { - case arg: Typed if isWildcardStarArg(arg) => - if (tree.fun.symbol.is(JavaDefined) && arg.expr.tpe.derivesFrom(defn.SeqClass)) - seqToArray(arg.expr) - else arg.expr - case arg => arg - } - transformTypeOfTree(cpy.Apply(tree)(tree.fun, args1)) - } - - /** Convert sequence argument to Java array */ - private def seqToArray(tree: Tree)(implicit ctx: Context): Tree = tree match { - case SeqLiteral(elems, elemtpt) => - JavaSeqLiteral(elems, elemtpt) - case _ => - val elemType = tree.tpe.elemType - var elemClass = elemType.classSymbol - if (defn.PhantomClasses contains elemClass) elemClass = defn.ObjectClass - ref(defn.DottyArraysModule) - .select(nme.seqToArray) - .appliedToType(elemType) - .appliedTo(tree, Literal(Constant(elemClass.typeRef))) - .ensureConforms(defn.ArrayOf(elemType)) - // Because of phantomclasses, the Java array's type might not conform to the return type - } - - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = - transformTypeOfTree(tree) - - /** If method overrides a Java varargs method, add a varargs bridge. - * Also transform trees inside method annotation - */ - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - assert(ctx.phase == thisTransformer) - def overridesJava = tree.symbol.allOverriddenSymbols.exists(_ is JavaDefined) - if (tree.symbol.info.isVarArgsMethod && overridesJava) - addVarArgsBridge(tree)(ctx.withPhase(thisTransformer.next)) - else - tree - } - - /** Add a Java varargs bridge - * @param ddef the original method definition which is assumed to override - * a Java varargs method JM up to this phase. - * @return a thicket consisting of `ddef` and a varargs bridge method - * which overrides the Java varargs method JM from this phase on - * and forwards to `ddef`. - */ - private def addVarArgsBridge(ddef: DefDef)(implicit ctx: Context): Tree = { - val original = ddef.symbol.asTerm - val bridge = original.copy( - flags = ddef.symbol.flags &~ Private | Artifact, - info = toJavaVarArgs(ddef.symbol.info)).enteredAfter(thisTransformer).asTerm - val bridgeDef = polyDefDef(bridge, trefs => vrefss => { - val (vrefs :+ varArgRef) :: vrefss1 = vrefss - val elemtp = varArgRef.tpe.widen.argTypes.head - ref(original.termRef) - .appliedToTypes(trefs) - .appliedToArgs(vrefs :+ TreeGen.wrapArray(varArgRef, elemtp)) - .appliedToArgss(vrefss1) - }) - Thicket(ddef, bridgeDef) - } - - /** Convert type from Scala to Java varargs method */ - private def toJavaVarArgs(tp: Type)(implicit ctx: Context): Type = tp match { - case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, toJavaVarArgs(tp.resultType)) - case tp: MethodType => - val inits :+ last = tp.paramTypes - val last1 = last.underlyingIfRepeated(isJava = true) - tp.derivedMethodType(tp.paramNames, inits :+ last1, tp.resultType) - } -} diff --git a/src/dotty/tools/dotc/transform/ElimStaticThis.scala b/src/dotty/tools/dotc/transform/ElimStaticThis.scala deleted file mode 100644 index 0601e0122..000000000 --- a/src/dotty/tools/dotc/transform/ElimStaticThis.scala +++ /dev/null @@ -1,40 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Contexts.Context -import Flags._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.SymDenotations.SymDenotation -import TreeTransforms.{MiniPhaseTransform, TransformerInfo} -import dotty.tools.dotc.core.Types.{ThisType, TermRef} - -/** Replace This references to module classes in static methods by global identifiers to the - * corresponding modules. - */ -class ElimStaticThis extends MiniPhaseTransform { - import ast.tpd._ - def phaseName: String = "elimStaticThis" - - override def transformThis(tree: This)(implicit ctx: Context, info: TransformerInfo): Tree = - if (!tree.symbol.is(Package) && ctx.owner.enclosingMethod.is(JavaStatic)) { - assert(tree.symbol.is(ModuleClass)) - ref(tree.symbol.sourceModule) - } - else tree - - override def transformIdent(tree: tpd.Ident)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - if (ctx.owner.enclosingMethod.is(JavaStatic)) { - tree.tpe match { - case TermRef(thiz: ThisType, _) if thiz.cls.is(ModuleClass) => - ref(thiz.cls.sourceModule).select(tree.symbol) - case TermRef(thiz: ThisType, _) => - assert(tree.symbol.is(Flags.JavaStatic)) - tree - case _ => tree - } - } - else tree - } -} diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala deleted file mode 100644 index 069176111..000000000 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ /dev/null @@ -1,664 +0,0 @@ -package dotty.tools.dotc -package transform - -import core.Phases._ -import core.DenotTransformers._ -import core.Denotations._ -import core.SymDenotations._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Names._ -import core.StdNames._ -import core.NameOps._ -import core.Decorators._ -import core.Constants._ -import typer.NoChecking -import typer.ProtoTypes._ -import typer.ErrorReporting._ -import core.TypeErasure._ -import core.Decorators._ -import dotty.tools.dotc.ast.{Trees, tpd, untpd} -import ast.Trees._ -import scala.collection.mutable.ListBuffer -import dotty.tools.dotc.core.{Constants, Flags} -import ValueClasses._ -import TypeUtils._ -import ExplicitOuter._ -import core.Mode - -class Erasure extends Phase with DenotTransformer { thisTransformer => - - override def phaseName: String = "erasure" - - /** List of names of phases that should precede this phase */ - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[InterceptedMethods], classOf[Splitter], classOf[ElimRepeated]) - - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { - case ref: SymDenotation => - assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}") - if (ref.symbol eq defn.ObjectClass) { - // Aftre erasure, all former Any members are now Object members - val ClassInfo(pre, _, ps, decls, selfInfo) = ref.info - val extendedScope = decls.cloneScope - for (decl <- defn.AnyClass.classInfo.decls) - if (!decl.isConstructor) extendedScope.enter(decl) - ref.copySymDenotation( - info = transformInfo(ref.symbol, - ClassInfo(pre, defn.ObjectClass, ps, extendedScope, selfInfo)) - ) - } - else { - val oldSymbol = ref.symbol - val newSymbol = - if ((oldSymbol.owner eq defn.AnyClass) && oldSymbol.isConstructor) - defn.ObjectClass.primaryConstructor - else oldSymbol - val oldOwner = ref.owner - val newOwner = if (oldOwner eq defn.AnyClass) defn.ObjectClass else oldOwner - val oldInfo = ref.info - val newInfo = transformInfo(ref.symbol, oldInfo) - val oldFlags = ref.flags - val newFlags = ref.flags &~ Flags.HasDefaultParams // HasDefaultParams needs to be dropped because overriding might become overloading - // TODO: define derivedSymDenotation? - if ((oldSymbol eq newSymbol) && (oldOwner eq newOwner) && (oldInfo eq newInfo) && (oldFlags == newFlags)) ref - else { - assert(!ref.is(Flags.PackageClass), s"trans $ref @ ${ctx.phase} oldOwner = $oldOwner, newOwner = $newOwner, oldInfo = $oldInfo, newInfo = $newInfo ${oldOwner eq newOwner} ${oldInfo eq newInfo}") - ref.copySymDenotation(symbol = newSymbol, owner = newOwner, initFlags = newFlags, info = newInfo) - } - } - case ref => - ref.derivedSingleDenotation(ref.symbol, transformInfo(ref.symbol, ref.info)) - } - - val eraser = new Erasure.Typer - - def run(implicit ctx: Context): Unit = { - val unit = ctx.compilationUnit - unit.tpdTree = eraser.typedExpr(unit.tpdTree)(ctx.fresh.setPhase(this.next)) - } - - override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context) = { - assertErased(tree) - tree match { - case res: tpd.This => - assert(!ExplicitOuter.referencesOuter(ctx.owner.enclosingClass, res), - i"Reference to $res from ${ctx.owner.showLocated}") - case ret: tpd.Return => - // checked only after erasure, as checking before erasure is complicated - // due presence of type params in returned types - val from = if (ret.from.isEmpty) ctx.owner.enclosingMethod else ret.from.symbol - val rType = from.info.finalResultType - assert(ret.expr.tpe <:< rType, - i"Returned value:${ret.expr} does not conform to result type(${ret.expr.tpe.widen} of method $from") - case _ => - } - } - - /** Assert that tree type and its widened underlying type are erased. - * Also assert that term refs have fixed symbols (so we are sure - * they need not be reloaded using member; this would likely fail as signatures - * may change after erasure). - */ - def assertErased(tree: tpd.Tree)(implicit ctx: Context): Unit = { - assertErased(tree.typeOpt, tree) - if (!defn.isPolymorphicAfterErasure(tree.symbol)) - assertErased(tree.typeOpt.widen, tree) - if (ctx.mode.isExpr) - tree.tpe match { - case ref: TermRef => - assert(ref.denot.isInstanceOf[SymDenotation] || - ref.denot.isInstanceOf[UniqueRefDenotation], - i"non-sym type $ref of class ${ref.getClass} with denot of class ${ref.denot.getClass} of $tree") - case _ => - } - } - - def assertErased(tp: Type, tree: tpd.Tree = tpd.EmptyTree)(implicit ctx: Context): Unit = - if (tp.typeSymbol == defn.ArrayClass && - ctx.compilationUnit.source.file.name == "Array.scala") {} // ok - else - assert(isErasedType(tp), - i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree : ${tree.tpe} / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase.prev}") -} - -object Erasure extends TypeTestsCasts{ - - import tpd._ - - object Boxing { - - def isUnbox(sym: Symbol)(implicit ctx: Context) = - sym.name == nme.unbox && sym.owner.linkedClass.isPrimitiveValueClass - - def isBox(sym: Symbol)(implicit ctx: Context) = - sym.name == nme.box && sym.owner.linkedClass.isPrimitiveValueClass - - def boxMethod(cls: ClassSymbol)(implicit ctx: Context) = - cls.linkedClass.info.member(nme.box).symbol - def unboxMethod(cls: ClassSymbol)(implicit ctx: Context) = - cls.linkedClass.info.member(nme.unbox).symbol - - /** Isf this tree is an unbox operation which can be safely removed - * when enclosed in a box, the unboxed argument, otherwise EmptyTree. - * Note that one can't always remove a Box(Unbox(x)) combination because the - * process of unboxing x may lead to throwing an exception. - * This is important for specialization: calls to the super constructor should not box/unbox specialized - * fields (see TupleX). (ID) - */ - private def safelyRemovableUnboxArg(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Apply(fn, arg :: Nil) - if isUnbox(fn.symbol) && defn.ScalaBoxedClasses().contains(arg.tpe.widen.typeSymbol) => - arg - case _ => - EmptyTree - } - - def constant(tree: Tree, const: Tree)(implicit ctx: Context) = - if (isPureExpr(tree)) const else Block(tree :: Nil, const) - - final def box(tree: Tree, target: => String = "")(implicit ctx: Context): Tree = ctx.traceIndented(i"boxing ${tree.showSummary}: ${tree.tpe} into $target") { - tree.tpe.widen match { - case ErasedValueType(tycon, _) => - New(tycon, cast(tree, underlyingOfValueClass(tycon.symbol.asClass)) :: Nil) // todo: use adaptToType? - case tp => - val cls = tp.classSymbol - if (cls eq defn.UnitClass) constant(tree, ref(defn.BoxedUnit_UNIT)) - else if (cls eq defn.NothingClass) tree // a non-terminating expression doesn't need boxing - else { - assert(cls ne defn.ArrayClass) - val arg = safelyRemovableUnboxArg(tree) - if (arg.isEmpty) ref(boxMethod(cls.asClass)).appliedTo(tree) - else { - ctx.log(s"boxing an unbox: ${tree.symbol} -> ${arg.tpe}") - arg - } - } - } - } - - def unbox(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"unboxing ${tree.showSummary}: ${tree.tpe} as a $pt") { - pt match { - case ErasedValueType(tycon, underlying) => - def unboxedTree(t: Tree) = - adaptToType(t, tycon) - .select(valueClassUnbox(tycon.symbol.asClass)) - .appliedToNone - - // Null unboxing needs to be treated separately since we cannot call a method on null. - // "Unboxing" null to underlying is equivalent to doing null.asInstanceOf[underlying] - // See tests/pos/valueclasses/nullAsInstanceOfVC.scala for cases where this might happen. - val tree1 = - if (tree.tpe isRef defn.NullClass) - adaptToType(tree, underlying) - else if (!(tree.tpe <:< tycon)) { - assert(!(tree.tpe.typeSymbol.isPrimitiveValueClass)) - val nullTree = Literal(Constant(null)) - val unboxedNull = adaptToType(nullTree, underlying) - - evalOnce(tree) { t => - If(t.select(defn.Object_eq).appliedTo(nullTree), - unboxedNull, - unboxedTree(t)) - } - } else unboxedTree(tree) - - cast(tree1, pt) - case _ => - val cls = pt.widen.classSymbol - if (cls eq defn.UnitClass) constant(tree, Literal(Constant(()))) - else { - assert(cls ne defn.ArrayClass) - ref(unboxMethod(cls.asClass)).appliedTo(tree) - } - } - } - - /** Generate a synthetic cast operation from tree.tpe to pt. - * Does not do any boxing/unboxing (this is handled upstream). - * Casts from and to ErasedValueType are special, see the explanation - * in ExtensionMethods#transform. - */ - def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { - // TODO: The commented out assertion fails for tailcall/t6574.scala - // Fix the problem and enable the assertion. - // assert(!pt.isInstanceOf[SingletonType], pt) - if (pt isRef defn.UnitClass) unbox(tree, pt) - else (tree.tpe, pt) match { - case (JavaArrayType(treeElem), JavaArrayType(ptElem)) - if treeElem.widen.isPrimitiveValueType && !ptElem.isPrimitiveValueType => - // See SI-2386 for one example of when this might be necessary. - cast(ref(defn.runtimeMethodRef(nme.toObjectArray)).appliedTo(tree), pt) - case (_, ErasedValueType(tycon, _)) => - ref(u2evt(tycon.symbol.asClass)).appliedTo(tree) - case _ => - tree.tpe.widen match { - case ErasedValueType(tycon, _) => - ref(evt2u(tycon.symbol.asClass)).appliedTo(tree) - case _ => - if (pt.isPrimitiveValueType) - primitiveConversion(tree, pt.classSymbol) - else - tree.asInstance(pt) - } - } - } - - /** Adaptation of an expression `e` to an expected type `PT`, applying the following - * rewritings exhaustively as long as the type of `e` is not a subtype of `PT`. - * - * e -> e() if `e` appears not as the function part of an application - * e -> box(e) if `e` is of erased value type - * e -> unbox(e, PT) otherwise, if `PT` is an erased value type - * e -> box(e) if `e` is of primitive type and `PT` is not a primitive type - * e -> unbox(e, PT) if `PT` is a primitive type and `e` is not of primitive type - * e -> cast(e, PT) otherwise - */ - def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree = - if (pt.isInstanceOf[FunProto]) tree - else tree.tpe.widen match { - case MethodType(Nil, _) if tree.isTerm => - adaptToType(tree.appliedToNone, pt) - case tpw => - if (pt.isInstanceOf[ProtoType] || tree.tpe <:< pt) - tree - else if (tpw.isErasedValueType) - adaptToType(box(tree), pt) - else if (pt.isErasedValueType) - adaptToType(unbox(tree, pt), pt) - else if (tpw.isPrimitiveValueType && !pt.isPrimitiveValueType) - adaptToType(box(tree), pt) - else if (pt.isPrimitiveValueType && !tpw.isPrimitiveValueType) - adaptToType(unbox(tree, pt), pt) - else - cast(tree, pt) - } - } - - class Typer extends typer.ReTyper with NoChecking { - import Boxing._ - - def erasedType(tree: untpd.Tree)(implicit ctx: Context): Type = { - val tp = tree.typeOpt - if (tree.isTerm) erasedRef(tp) else valueErasure(tp) - } - - override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = { - assert(tree.hasType) - val erased = erasedType(tree) - ctx.log(s"promoting ${tree.show}: ${erased.showWithUnderlying()}") - tree.withType(erased) - } - - /** When erasing most TypeTrees we should not semi-erase value types. - * This is not the case for [[DefDef#tpt]], [[ValDef#tpt]] and [[Typed#tpt]], they - * are handled separately by [[typedDefDef]], [[typedValDef]] and [[typedTyped]]. - */ - override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = - tree.withType(erasure(tree.tpe)) - - /** This override is only needed to semi-erase type ascriptions */ - override def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = { - val Typed(expr, tpt) = tree - val tpt1 = promote(tpt) - val expr1 = typed(expr, tpt1.tpe) - assignType(untpd.cpy.Typed(tree)(expr1, tpt1), tpt1) - } - - override def typedLiteral(tree: untpd.Literal)(implicit ctx: Context): Literal = - if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt) - else if (tree.const.tag == Constants.ClazzTag) Literal(Constant(erasure(tree.const.typeValue))) - else super.typedLiteral(tree) - - /** Type check select nodes, applying the following rewritings exhaustively - * on selections `e.m`, where `OT` is the type of the owner of `m` and `ET` - * is the erased type of the selection's original qualifier expression. - * - * e.m1 -> e.m2 if `m1` is a member of Any or AnyVal and `m2` is - * the same-named member in Object. - * e.m -> box(e).m if `e` is primitive and `m` is a member or a reference class - * or `e` has an erased value class type. - * e.m -> unbox(e).m if `e` is not primitive and `m` is a member of a primtive type. - * e.m -> cast(e, OT).m if the type of `e` does not conform to OT and `m` - * is not an array operation. - * - * If `m` is an array operation, i.e. one of the members apply, update, length, clone, and - * <init> of class Array, we additionally try the following rewritings: - * - * e.m -> runtime.array_m(e) if ET is Object - * e.m -> cast(e, ET).m if the type of `e` does not conform to ET - * e.clone -> e.clone' where clone' is Object's clone method - * e.m -> e.[]m if `m` is an array operation other than `clone`. - */ - override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { - val sym = tree.symbol - assert(sym.exists, tree.show) - - def select(qual: Tree, sym: Symbol): Tree = { - val name = tree.typeOpt match { - case tp: NamedType if tp.name.isShadowedName => sym.name.shadowedName - case _ => sym.name - } - untpd.cpy.Select(tree)(qual, sym.name) - .withType(NamedType.withFixedSym(qual.tpe, sym)) - } - - def selectArrayMember(qual: Tree, erasedPre: Type): Tree = - if (erasedPre isRef defn.ObjectClass) - runtimeCallWithProtoArgs(tree.name.genericArrayOp, pt, qual) - else if (!(qual.tpe <:< erasedPre)) - selectArrayMember(cast(qual, erasedPre), erasedPre) - else - assignType(untpd.cpy.Select(tree)(qual, tree.name.primitiveArrayOp), qual) - - def adaptIfSuper(qual: Tree): Tree = qual match { - case Super(thisQual, untpd.EmptyTypeIdent) => - val SuperType(thisType, supType) = qual.tpe - if (sym.owner is Flags.Trait) - cpy.Super(qual)(thisQual, untpd.Ident(sym.owner.asClass.name)) - .withType(SuperType(thisType, sym.owner.typeRef)) - else - qual.withType(SuperType(thisType, thisType.firstParent)) - case _ => - qual - } - - def recur(qual: Tree): Tree = { - val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType - val symIsPrimitive = sym.owner.isPrimitiveValueClass - if ((sym.owner eq defn.AnyClass) || (sym.owner eq defn.AnyValClass)) { - assert(sym.isConstructor, s"${sym.showLocated}") - select(qual, defn.ObjectClass.info.decl(sym.name).symbol) - } - else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType) - recur(box(qual)) - else if (!qualIsPrimitive && symIsPrimitive) - recur(unbox(qual, sym.owner.typeRef)) - else if (sym.owner eq defn.ArrayClass) - selectArrayMember(qual, erasure(tree.qualifier.typeOpt.widen.finalResultType)) - else { - val qual1 = adaptIfSuper(qual) - if (qual1.tpe.derivesFrom(sym.owner) || qual1.isInstanceOf[Super]) - select(qual1, sym) - else - recur(cast(qual1, sym.owner.typeRef)) - } - } - - recur(typed(tree.qualifier, AnySelectionProto)) - } - - override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = - if (tree.symbol == ctx.owner.enclosingClass || tree.symbol.isStaticOwner) promote(tree) - else { - ctx.log(i"computing outer path from ${ctx.owner.ownersIterator.toList}%, % to ${tree.symbol}, encl class = ${ctx.owner.enclosingClass}") - outer.path(tree.symbol) - } - - private def runtimeCallWithProtoArgs(name: Name, pt: Type, args: Tree*)(implicit ctx: Context): Tree = { - val meth = defn.runtimeMethodRef(name) - val followingParams = meth.symbol.info.firstParamTypes.drop(args.length) - val followingArgs = protoArgs(pt).zipWithConserve(followingParams)(typedExpr).asInstanceOf[List[tpd.Tree]] - ref(meth).appliedToArgs(args.toList ++ followingArgs) - } - - private def protoArgs(pt: Type): List[untpd.Tree] = pt match { - case pt: FunProto => pt.args ++ protoArgs(pt.resType) - case _ => Nil - } - - override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context) = { - val ntree = interceptTypeApply(tree.asInstanceOf[TypeApply])(ctx.withPhase(ctx.erasurePhase)) - - ntree match { - case TypeApply(fun, args) => - val fun1 = typedExpr(fun, WildcardType) - fun1.tpe.widen match { - case funTpe: PolyType => - val args1 = args.mapconserve(typedType(_)) - untpd.cpy.TypeApply(tree)(fun1, args1).withType(funTpe.instantiate(args1.tpes)) - case _ => fun1 - } - case _ => typedExpr(ntree, pt) - } - } - - override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { - val Apply(fun, args) = tree - if (fun.symbol == defn.dummyApply) - typedUnadapted(args.head, pt) - else typedExpr(fun, FunProto(args, pt, this)) match { - case fun1: Apply => // arguments passed in prototype were already passed - fun1 - case fun1 => - fun1.tpe.widen match { - case mt: MethodType => - val outers = outer.args(fun.asInstanceOf[tpd.Tree]) // can't use fun1 here because its type is already erased - val args1 = (outers ::: args ++ protoArgs(pt)).zipWithConserve(mt.paramTypes)(typedExpr) - untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType - case _ => - throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") - } - } - } - - // The following four methods take as the proto-type the erasure of the pre-existing type, - // if the original proto-type is not a value type. - // This makes all branches be adapted to the correct type. - override def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context) = - super.typedSeqLiteral(tree, erasure(tree.typeOpt)) - // proto type of typed seq literal is original type; - - override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = - super.typedIf(tree, adaptProto(tree, pt)) - - override def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = - super.typedMatch(tree, adaptProto(tree, pt)) - - override def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context) = - super.typedTry(tree, adaptProto(tree, pt)) - - private def adaptProto(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = { - if (pt.isValueType) pt else { - if (tree.typeOpt.derivesFrom(ctx.definitions.UnitClass)) - tree.typeOpt - else valueErasure(tree.typeOpt) - } - } - - override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context): ValDef = - super.typedValDef(untpd.cpy.ValDef(vdef)( - tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym) - - override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { - val restpe = - if (sym.isConstructor) defn.UnitType - else sym.info.resultType - val ddef1 = untpd.cpy.DefDef(ddef)( - tparams = Nil, - vparamss = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil, - tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)), - rhs = ddef.rhs match { - case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) - case _ => ddef.rhs - }) - super.typedDefDef(ddef1, sym) - } - - /** After erasure, we may have to replace the closure method by a bridge. - * LambdaMetaFactory handles this automatically for most types, but we have - * to deal with boxing and unboxing of value classes ourselves. - */ - override def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = { - val implClosure @ Closure(_, meth, _) = super.typedClosure(tree, pt) - implClosure.tpe match { - case SAMType(sam) => - val implType = meth.tpe.widen - - val List(implParamTypes) = implType.paramTypess - val List(samParamTypes) = sam.info.paramTypess - val implResultType = implType.resultType - val samResultType = sam.info.resultType - - // Given a value class V with an underlying type U, the following code: - // val f: Function1[V, V] = x => ... - // results in the creation of a closure and a method: - // def $anonfun(v1: V): V = ... - // val f: Function1[V, V] = closure($anonfun) - // After [[Erasure]] this method will look like: - // def $anonfun(v1: ErasedValueType(V, U)): ErasedValueType(V, U) = ... - // And after [[ElimErasedValueType]] it will look like: - // def $anonfun(v1: U): U = ... - // This method does not implement the SAM of Function1[V, V] anymore and - // needs to be replaced by a bridge: - // def $anonfun$2(v1: V): V = new V($anonfun(v1.underlying)) - // val f: Function1 = closure($anonfun$2) - // In general, a bridge is needed when the signature of the closure method after - // Erasure contains an ErasedValueType but the corresponding type in the functional - // interface is not an ErasedValueType. - val bridgeNeeded = - (implResultType :: implParamTypes, samResultType :: samParamTypes).zipped.exists( - (implType, samType) => implType.isErasedValueType && !samType.isErasedValueType - ) - - if (bridgeNeeded) { - val bridge = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Flags.Synthetic | Flags.Method, sam.info) - val bridgeCtx = ctx.withOwner(bridge) - Closure(bridge, bridgeParamss => { - implicit val ctx: Context = bridgeCtx - - val List(bridgeParams) = bridgeParamss - val rhs = Apply(meth, (bridgeParams, implParamTypes).zipped.map(adapt(_, _))) - adapt(rhs, sam.info.resultType) - }) - } else implClosure - case _ => - implClosure - } - } - - override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = - EmptyTree - - override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { - val stats1 = Trees.flatten(super.typedStats(stats, exprOwner)) - if (ctx.owner.isClass) stats1 ::: addBridges(stats, stats1)(ctx) else stats1 - } - - // this implementation doesn't check for bridge clashes with value types! - def addBridges(oldStats: List[untpd.Tree], newStats: List[tpd.Tree])(implicit ctx: Context): List[tpd.Tree] = { - val beforeCtx = ctx.withPhase(ctx.erasurePhase) - def traverse(after: List[Tree], before: List[untpd.Tree], - emittedBridges: ListBuffer[tpd.DefDef] = ListBuffer[tpd.DefDef]()): List[tpd.DefDef] = { - after match { - case Nil => emittedBridges.toList - case (member: DefDef) :: newTail => - before match { - case Nil => emittedBridges.toList - case (oldMember: untpd.DefDef) :: oldTail => - try { - val oldSymbol = oldMember.symbol(beforeCtx) - val newSymbol = member.symbol(ctx) - assert(oldSymbol.name(beforeCtx) == newSymbol.name, - s"${oldSymbol.name(beforeCtx)} bridging with ${newSymbol.name}") - val newOverridden = oldSymbol.denot.allOverriddenSymbols.toSet // TODO: clarify new <-> old in a comment; symbols are swapped here - val oldOverridden = newSymbol.allOverriddenSymbols(beforeCtx).toSet // TODO: can we find a more efficient impl? newOverridden does not have to be a set! - def stillInBaseClass(sym: Symbol) = ctx.owner derivesFrom sym.owner - val neededBridges = (oldOverridden -- newOverridden).filter(stillInBaseClass) - - var minimalSet = Set[Symbol]() - // compute minimal set of bridges that are needed: - for (bridge <- neededBridges) { - val isRequired = minimalSet.forall(nxtBridge => !(bridge.info =:= nxtBridge.info)) - - if (isRequired) { - // check for clashes - val clash: Option[Symbol] = oldSymbol.owner.info.decls.lookupAll(bridge.name).find { - sym => - (sym.name eq bridge.name) && sym.info.widen =:= bridge.info.widen - }.orElse( - emittedBridges.find(stat => (stat.name == bridge.name) && stat.tpe.widen =:= bridge.info.widen) - .map(_.symbol)) - clash match { - case Some(cl) => - ctx.error(i"bridge for method ${newSymbol.showLocated(beforeCtx)} of type ${newSymbol.info(beforeCtx)}\n" + - i"clashes with ${cl.symbol.showLocated(beforeCtx)} of type ${cl.symbol.info(beforeCtx)}\n" + - i"both have same type after erasure: ${bridge.symbol.info}") - case None => minimalSet += bridge - } - } - } - - val bridgeImplementations = minimalSet.map { - sym => makeBridgeDef(member, sym)(ctx) - } - emittedBridges ++= bridgeImplementations - } catch { - case ex: MergeError => ctx.error(ex.getMessage, member.pos) - } - - traverse(newTail, oldTail, emittedBridges) - case notADefDef :: oldTail => - traverse(after, oldTail, emittedBridges) - } - case notADefDef :: newTail => - traverse(newTail, before, emittedBridges) - } - } - - traverse(newStats, oldStats) - } - - private final val NoBridgeFlags = Flags.Accessor | Flags.Deferred | Flags.Lazy | Flags.ParamAccessor - - /** Create a bridge DefDef which overrides a parent method. - * - * @param newDef The DefDef which needs bridging because its signature - * does not match the parent method signature - * @param parentSym A symbol corresponding to the parent method to override - * @return A new DefDef whose signature matches the parent method - * and whose body only contains a call to newDef - */ - def makeBridgeDef(newDef: tpd.DefDef, parentSym: Symbol)(implicit ctx: Context): tpd.DefDef = { - val newDefSym = newDef.symbol - val currentClass = newDefSym.owner.asClass - - def error(reason: String) = { - assert(false, s"failure creating bridge from ${newDefSym} to ${parentSym}, reason: $reason") - ??? - } - var excluded = NoBridgeFlags - if (!newDefSym.is(Flags.Protected)) excluded |= Flags.Protected // needed to avoid "weaker access" assertion failures in expandPrivate - val bridge = ctx.newSymbol(currentClass, - parentSym.name, parentSym.flags &~ excluded | Flags.Bridge, parentSym.info, coord = newDefSym.owner.coord).asTerm - bridge.enteredAfter(ctx.phase.prev.asInstanceOf[DenotTransformer]) // this should be safe, as we're executing in context of next phase - ctx.debuglog(s"generating bridge from ${newDefSym} to $bridge") - - val sel: Tree = This(currentClass).select(newDefSym.termRef) - - val resultType = parentSym.info.widen.resultType - - val bridgeCtx = ctx.withOwner(bridge) - - tpd.DefDef(bridge, { paramss: List[List[tpd.Tree]] => - implicit val ctx: Context = bridgeCtx - - val rhs = paramss.foldLeft(sel)((fun, vparams) => - fun.tpe.widen match { - case MethodType(names, types) => Apply(fun, (vparams, types).zipped.map(adapt(_, _, untpd.EmptyTree))) - case a => error(s"can not resolve apply type $a") - - }) - adapt(rhs, resultType) - }) - } - - override def adapt(tree: Tree, pt: Type, original: untpd.Tree)(implicit ctx: Context): Tree = - ctx.traceIndented(i"adapting ${tree.showSummary}: ${tree.tpe} to $pt", show = true) { - assert(ctx.phase == ctx.erasurePhase.next, ctx.phase) - if (tree.isEmpty) tree - else if (ctx.mode is Mode.Pattern) tree // TODO: replace with assertion once pattern matcher is active - else adaptToType(tree, pt) - } - } -} diff --git a/src/dotty/tools/dotc/transform/ExpandPrivate.scala b/src/dotty/tools/dotc/transform/ExpandPrivate.scala deleted file mode 100644 index 83cd395ff..000000000 --- a/src/dotty/tools/dotc/transform/ExpandPrivate.scala +++ /dev/null @@ -1,111 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer} -import Contexts.Context -import Symbols._ -import Scopes._ -import Flags._ -import StdNames._ -import SymDenotations._ -import Types._ -import collection.mutable -import TreeTransforms._ -import Decorators._ -import ast.Trees._ -import TreeTransforms._ -import java.io.File.separatorChar -import ValueClasses._ - -/** Make private term members that are accessed from another class - * non-private by resetting the Private flag and expanding their name. - * - * Make private accessor in value class not-private. Ihis is necessary to unbox - * the value class when accessing it from separate compilation units - * - * Also, make non-private any private parameter forwarders that forward to an inherited - * public or protected parameter accessor with the same name as the forwarder. - * This is necessary since private methods are not allowed to have the same name - * as inherited public ones. - * - * See discussion in https://github.com/lampepfl/dotty/pull/784 - * and https://github.com/lampepfl/dotty/issues/783 - */ -class ExpandPrivate extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - override def phaseName: String = "expandPrivate" - - override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { - tree match { - case t: DefDef => - val sym = t.symbol - def hasWeakerAccess(other: Symbol) = { - // public > protected > /* default */ > private - if (sym.is(Private)) other.is(Private) - else if (sym.is(Protected)) other.is(Protected | Private) - else true // sym is public - } - val fail = sym.allOverriddenSymbols.findSymbol(x => !hasWeakerAccess(x)) - if (fail.exists) { - assert(false, i"${sym.showFullName}: ${sym.info} has weaker access than superclass method ${fail.showFullName}: ${fail.info}") - } - case _ => - } - } - - private def isVCPrivateParamAccessor(d: SymDenotation)(implicit ctx: Context) = - d.isTerm && d.is(PrivateParamAccessor) && isDerivedValueClass(d.owner) - - /** Make private terms accessed from different classes non-private. - * Note: this happens also for accesses between class and linked module class. - * If we change the scheme at one point to make static module class computations - * static members of the companion class, we should tighten the condition below. - */ - private def ensurePrivateAccessible(d: SymDenotation)(implicit ctx: Context) = - if (isVCPrivateParamAccessor(d)) - d.ensureNotPrivate.installAfter(thisTransform) - else if (d.is(PrivateTerm) && d.owner != ctx.owner.enclosingClass) { - // Paths `p1` and `p2` are similar if they have a common suffix that follows - // possibly different directory paths. That is, their common suffix extends - // in both cases either to the start of the path or to a file separator character. - def isSimilar(p1: String, p2: String): Boolean = { - var i = p1.length - 1 - var j = p2.length - 1 - while (i >= 0 && j >= 0 && p1(i) == p2(j) && p1(i) != separatorChar) { - i -= 1 - j -= 1 - } - (i < 0 || p1(i) == separatorChar) && - (j < 0 || p1(j) == separatorChar) - } - assert(isSimilar(d.symbol.sourceFile.path, ctx.source.file.path), - i"private ${d.symbol.showLocated} in ${d.symbol.sourceFile} accessed from ${ctx.owner.showLocated} in ${ctx.source.file}") - d.ensureNotPrivate.installAfter(thisTransform) - } - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { - ensurePrivateAccessible(tree.symbol) - tree - } - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = { - ensurePrivateAccessible(tree.symbol) - tree - } - - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = { - val sym = tree.symbol - tree.rhs match { - case Apply(sel @ Select(_: Super, _), _) - if sym.is(PrivateParamAccessor) && sel.symbol.is(ParamAccessor) && sym.name == sel.symbol.name => - sym.ensureNotPrivate.installAfter(thisTransform) - case _ => - if (isVCPrivateParamAccessor(sym)) - sym.ensureNotPrivate.installAfter(thisTransform) - } - tree - } -} diff --git a/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/src/dotty/tools/dotc/transform/ExpandSAMs.scala deleted file mode 100644 index 91399f91a..000000000 --- a/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ /dev/null @@ -1,86 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Contexts._, Symbols._, Types._, Flags._, Decorators._, StdNames._, Constants._ -import SymDenotations.SymDenotation -import TreeTransforms._ -import SymUtils._ -import ast.untpd -import ast.Trees._ - -/** Expand SAM closures that cannot be represented by the JVM as lambdas to anonymous classes. - * These fall into five categories - * - * 1. Partial function closures, we need to generate a isDefinedAt method for these. - * 2. Closures implementing non-trait classes. - * 3. Closures implementing classes that inherit from a class other than Object - * (a lambda cannot not be a run-time subtype of such a class) - * 4. Closures that implement traits which run initialization code. - * 5. Closures that get synthesized abstract methods in the transformation pipeline. These methods can be - * (1) superaccessors, (2) outer references, (3) accessors for fields. - */ -class ExpandSAMs extends MiniPhaseTransform { thisTransformer => - override def phaseName = "expandSAMs" - - import ast.tpd._ - - /** Is the SAMType `cls` also a SAM under the rules of the platform? */ - def isPlatformSam(cls: ClassSymbol)(implicit ctx: Context): Boolean = - ctx.platform.isSam(cls) - - override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { - case Block(stats @ (fn: DefDef) :: Nil, Closure(_, fnRef, tpt)) if fnRef.symbol == fn.symbol => - tpt.tpe match { - case NoType => tree // it's a plain function - case tpe @ SAMType(_) if tpe.isRef(defn.PartialFunctionClass) => - toPartialFunction(tree) - case tpe @ SAMType(_) if isPlatformSam(tpe.classSymbol.asClass) => - tree - case tpe => - val Seq(samDenot) = tpe.abstractTermMembers.filter(!_.symbol.is(SuperAccessor)) - cpy.Block(tree)(stats, - AnonClass(tpe :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil)) - } - case _ => - tree - } - - private def toPartialFunction(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = { - val Block( - (applyDef @ DefDef(nme.ANON_FUN, Nil, List(List(param)), _, _)) :: Nil, - Closure(_, _, tpt)) = tree - val applyRhs: Tree = applyDef.rhs - val applyFn = applyDef.symbol.asTerm - - val MethodType(paramNames, paramTypes) = applyFn.info - val isDefinedAtFn = applyFn.copy( - name = nme.isDefinedAt, - flags = Synthetic | Method, - info = MethodType(paramNames, paramTypes, defn.BooleanType)).asTerm - val tru = Literal(Constant(true)) - def isDefinedAtRhs(paramRefss: List[List[Tree]]) = applyRhs match { - case Match(selector, cases) => - assert(selector.symbol == param.symbol) - val paramRef = paramRefss.head.head - // Again, the alternative - // val List(List(paramRef)) = paramRefs - // fails with a similar self instantiation error - def translateCase(cdef: CaseDef): CaseDef = - cpy.CaseDef(cdef)(body = tru).changeOwner(applyFn, isDefinedAtFn) - val defaultSym = ctx.newSymbol(isDefinedAtFn, nme.WILDCARD, Synthetic, selector.tpe.widen) - val defaultCase = - CaseDef( - Bind(defaultSym, Underscore(selector.tpe.widen)), - EmptyTree, - Literal(Constant(false))) - val annotated = Annotated(paramRef, New(ref(defn.UncheckedAnnotType))) - cpy.Match(applyRhs)(annotated, cases.map(translateCase) :+ defaultCase) - case _ => - tru - } - val isDefinedAtDef = transformFollowingDeep(DefDef(isDefinedAtFn, isDefinedAtRhs(_))) - val anonCls = AnonClass(tpt.tpe :: Nil, List(applyFn, isDefinedAtFn), List(nme.apply, nme.isDefinedAt)) - cpy.Block(tree)(List(applyDef, isDefinedAtDef), anonCls) - } -} diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala deleted file mode 100644 index 3fec47e9f..000000000 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ /dev/null @@ -1,362 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import core.StdNames.nme -import core.Names._ -import core.NameOps._ -import ast.Trees._ -import SymUtils._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Phases.Phase -import util.Property -import collection.mutable - -/** This phase adds outer accessors to classes and traits that need them. - * Compared to Scala 2.x, it tries to minimize the set of classes - * that take outer accessors by scanning class implementations for - * outer references. - * - * The following things are delayed until erasure and are performed - * by class OuterOps: - * - * - add outer parameters to constructors - * - pass outer arguments in constructor calls - * - * replacement of outer this by outer paths is done in Erasure. - * needs to run after pattern matcher as it can add outer checks and force creation of $outer - */ -class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransformer => - import ExplicitOuter._ - import ast.tpd._ - - val Outer = new Property.Key[Tree] - - override def phaseName: String = "explicitOuter" - - /** List of names of phases that should have finished their processing of all compilation units - * before this phase starts - */ - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PatternMatcher]) - - /** Add outer accessors if a class always needs an outer pointer */ - override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { - case tp @ ClassInfo(_, cls, _, decls, _) if needsOuterAlways(cls) && !sym.is(JavaDefined) => - val newDecls = decls.cloneScope - newOuterAccessors(cls).foreach(newDecls.enter) - tp.derivedClassInfo(decls = newDecls) - case _ => - tp - } - - override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isClass - - /** Convert a selection of the form `qual.C_<OUTER>` to an outer path from `qual` to `C` */ - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = - if (tree.name.isOuterSelect) - outer.path(tree.tpe.widen.classSymbol, tree.qualifier).ensureConforms(tree.tpe) - else tree - - /** First, add outer accessors if a class does not have them yet and it references an outer this. - * If the class has outer accessors, implement them. - * Furthermore, if a parent trait might have an outer accessor, - * provide an implementation for the outer accessor by computing the parent's - * outer from the parent type prefix. If the trait ends up not having an outer accessor - * after all, the implementation is redundant, but does not harm. - * The same logic is not done for non-trait parent classes because for them the outer - * pointer is passed in the super constructor, which will be implemented later in - * a separate phase which needs to run after erasure. However, we make sure here - * that the super class constructor is indeed a New, and not just a type. - */ - override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { - val cls = ctx.owner.asClass - val isTrait = cls.is(Trait) - if (needsOuterIfReferenced(cls) && - !needsOuterAlways(cls) && - impl.existsSubTree(referencesOuter(cls, _))) - ensureOuterAccessors(cls) - if (hasOuter(cls)) { - val newDefs = new mutable.ListBuffer[Tree] - if (isTrait) - newDefs += DefDef(outerAccessor(cls).asTerm, EmptyTree) - else { - val outerParamAcc = outerParamAccessor(cls) - newDefs += ValDef(outerParamAcc, EmptyTree) - newDefs += DefDef(outerAccessor(cls).asTerm, ref(outerParamAcc)) - } - - for (parentTrait <- cls.mixins) { - if (needsOuterIfReferenced(parentTrait)) { - val parentTp = cls.denot.thisType.baseTypeRef(parentTrait) - val outerAccImpl = newOuterAccessor(cls, parentTrait).enteredAfter(thisTransformer) - newDefs += DefDef(outerAccImpl, singleton(outerPrefix(parentTp))) - } - } - - val parents1 = - for (parent <- impl.parents) yield { - val parentCls = parent.tpe.classSymbol.asClass - if (parentCls.is(Trait)) { - parent - } - else parent match { // ensure class parent is a constructor - case parent: TypeTree => New(parent.tpe, Nil).withPos(impl.pos) - case _ => parent - } - } - cpy.Template(impl)(parents = parents1, body = impl.body ++ newDefs) - } - else impl - } - - override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - if (tree.tpt ne EmptyTree) { - val cls = tree.tpt.asInstanceOf[TypeTree].tpe.classSymbol - if (cls.exists && hasOuter(cls.asClass)) - ctx.error("Not a single abstract method type, requires an outer pointer", tree.pos) - } - tree - } -} - -object ExplicitOuter { - import ast.tpd._ - - /** Ensure that class `cls` has outer accessors */ - def ensureOuterAccessors(cls: ClassSymbol)(implicit ctx: Context): Unit = { - //todo: implementing #165 would simplify this logic - val prevPhase = ctx.phase.prev - assert(prevPhase.id <= ctx.explicitOuterPhase.id, "can add $outer symbols only before ExplicitOuter") - assert(prevPhase.isInstanceOf[DenotTransformer], "adding outerAccessors requires being DenotTransformer") - if (!hasOuter(cls)) { - newOuterAccessors(cls).foreach(_.enteredAfter(prevPhase.asInstanceOf[DenotTransformer])) - } - } - - /** The outer accessor and potentially outer param accessor needed for class `cls` */ - private def newOuterAccessors(cls: ClassSymbol)(implicit ctx: Context) = - newOuterAccessor(cls, cls) :: (if (cls is Trait) Nil else newOuterParamAccessor(cls) :: Nil) - - /** A new outer accessor or param accessor */ - private def newOuterSym(owner: ClassSymbol, cls: ClassSymbol, name: TermName, flags: FlagSet)(implicit ctx: Context) = { - val target = cls.owner.enclosingClass.typeRef - val info = if (flags.is(Method)) ExprType(target) else target - ctx.newSymbol(owner, name, Synthetic | flags, info, coord = cls.coord) - } - - /** A new param accessor for the outer field in class `cls` */ - private def newOuterParamAccessor(cls: ClassSymbol)(implicit ctx: Context) = - newOuterSym(cls, cls, nme.OUTER, Private | ParamAccessor) - - /** A new outer accessor for class `cls` which is a member of `owner` */ - private def newOuterAccessor(owner: ClassSymbol, cls: ClassSymbol)(implicit ctx: Context) = { - val deferredIfTrait = if (owner.is(Trait)) Deferred else EmptyFlags - val outerAccIfOwn = if (owner == cls) OuterAccessor else EmptyFlags - newOuterSym(owner, cls, outerAccName(cls), - Final | Method | Stable | outerAccIfOwn | deferredIfTrait) - } - - private def outerAccName(cls: ClassSymbol)(implicit ctx: Context): TermName = - nme.OUTER.expandedName(cls) - - /** Class needs an outer pointer, provided there is a reference to an outer this in it. */ - def needsOuterIfReferenced(cls: ClassSymbol)(implicit ctx: Context): Boolean = - !(cls.isStatic || - cls.owner.enclosingClass.isStaticOwner || - cls.is(PureInterface) - ) - - /** Class unconditionally needs an outer pointer. This is the case if - * the class needs an outer pointer if referenced and one of the following holds: - * - we might not know at all instantiation sites whether outer is referenced or not - * - we need to potentially pass along outer to a parent class or trait - */ - private def needsOuterAlways(cls: ClassSymbol)(implicit ctx: Context): Boolean = - needsOuterIfReferenced(cls) && - (!hasLocalInstantiation(cls) || // needs outer because we might not know whether outer is referenced or not - cls.classInfo.parents.exists(parent => // needs outer to potentially pass along to parent - needsOuterIfReferenced(parent.classSymbol.asClass))) - - /** Class is always instantiated in the compilation unit where it is defined */ - private def hasLocalInstantiation(cls: ClassSymbol)(implicit ctx: Context): Boolean = - // scala2x modules always take an outer pointer(as of 2.11) - // dotty modules are always locally instantiated - cls.owner.isTerm || cls.is(Private) || cls.is(Module, butNot = Scala2x) - - /** The outer parameter accessor of cass `cls` */ - private def outerParamAccessor(cls: ClassSymbol)(implicit ctx: Context): TermSymbol = - cls.info.decl(nme.OUTER).symbol.asTerm - - /** The outer accessor of class `cls`. To find it is a bit tricky. The - * class might have been moved with new owners between ExplicitOuter and Erasure, - * where the method is also called. For instance, it might have been part - * of a by-name argument, and therefore be moved under a closure method - * by ElimByName. In that case looking up the method again at Erasure with the - * fully qualified name `outerAccName` will fail, because the `outerAccName`'s - * result is phase dependent. In that case we use a backup strategy where we search all - * definitions in the class to find the one with the OuterAccessor flag. - */ - def outerAccessor(cls: ClassSymbol)(implicit ctx: Context): Symbol = - if (cls.isStatic) NoSymbol // fast return to avoid scanning package decls - else cls.info.member(outerAccName(cls)).suchThat(_ is OuterAccessor).symbol orElse - cls.info.decls.find(_ is OuterAccessor).getOrElse(NoSymbol) - - /** Class has an outer accessor. Can be called only after phase ExplicitOuter. */ - private def hasOuter(cls: ClassSymbol)(implicit ctx: Context): Boolean = - needsOuterIfReferenced(cls) && outerAccessor(cls).exists - - /** Class constructor takes an outer argument. Can be called only after phase ExplicitOuter. */ - private def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean = - !cls.is(Trait) && needsOuterIfReferenced(cls) && outerAccessor(cls).exists - - /** Tree references an outer class of `cls` which is not a static owner. - */ - def referencesOuter(cls: Symbol, tree: Tree)(implicit ctx: Context): Boolean = { - def isOuterSym(sym: Symbol) = - !sym.isStaticOwner && cls.isProperlyContainedIn(sym) - def isOuterRef(ref: Type): Boolean = ref match { - case ref: ThisType => - isOuterSym(ref.cls) - case ref: TermRef => - if (ref.prefix ne NoPrefix) - !ref.symbol.isStatic && isOuterRef(ref.prefix) - else ( - (ref.symbol is Hoistable) && - // ref.symbol will be placed in enclosing class scope by LambdaLift, so it might need - // an outer path then. - isOuterSym(ref.symbol.owner.enclosingClass) - || - // If not hoistable, ref.symbol will get a proxy in immediately enclosing class. If this properly - // contains the current class, it needs an outer path. - // If the symbol is hoistable, it might have free variables for which the same - // reasoning applies. See pos/i1664.scala - ctx.owner.enclosingClass.owner.enclosingClass.isContainedIn(ref.symbol.owner) - ) - case _ => false - } - def hasOuterPrefix(tp: Type) = tp match { - case TypeRef(prefix, _) => isOuterRef(prefix) - case _ => false - } - tree match { - case _: This | _: Ident => isOuterRef(tree.tpe) - case nw: New => - val newCls = nw.tpe.classSymbol - isOuterSym(newCls.owner.enclosingClass) || - hasOuterPrefix(nw.tpe) || - newCls.owner.isTerm && cls.isProperlyContainedIn(newCls) - // newCls might get proxies for free variables. If current class is - // properly contained in newCls, it needs an outer path to newCls access the - // proxies and forward them to the new instance. - case _ => - false - } - } - - private final val Hoistable = Method | Lazy | Module - - /** The outer prefix implied by type `tpe` */ - private def outerPrefix(tpe: Type)(implicit ctx: Context): Type = tpe match { - case tpe: TypeRef => - tpe.symbol match { - case cls: ClassSymbol => - if (tpe.prefix eq NoPrefix) cls.owner.enclosingClass.thisType - else tpe.prefix - case _ => - outerPrefix(tpe.underlying) - } - case tpe: TypeProxy => - outerPrefix(tpe.underlying) - } - - def outer(implicit ctx: Context): OuterOps = new OuterOps(ctx) - - /** The operations in this class - * - add outer parameters - * - pass outer arguments to these parameters - * - replace outer this references by outer paths. - * They are called from erasure. There are two constraints which - * suggest these operations should be done in erasure. - * - Replacing this references with outer paths loses aliasing information, - * so programs will not typecheck with unerased types unless a lot of type - * refinements are added. Therefore, outer paths should be computed no - * earlier than erasure. - * - outer parameters should not show up in signatures, so again - * they cannot be added before erasure. - * - outer arguments need access to outer parameters as well as to the - * original type prefixes of types in New expressions. These prefixes - * get erased during erasure. Therefore, outer arguments have to be passed - * no later than erasure. - */ - class OuterOps(val ictx: Context) extends AnyVal { - private implicit def ctx: Context = ictx - - /** If `cls` has an outer parameter add one to the method type `tp`. */ - def addParam(cls: ClassSymbol, tp: Type): Type = - if (hasOuterParam(cls)) { - val mt @ MethodType(pnames, ptypes) = tp - mt.derivedMethodType( - nme.OUTER :: pnames, cls.owner.enclosingClass.typeRef :: ptypes, mt.resultType) - } else tp - - /** If function in an apply node is a constructor that needs to be passed an - * outer argument, the singleton list with the argument, otherwise Nil. - */ - def args(fun: Tree): List[Tree] = { - if (fun.symbol.isConstructor) { - val cls = fun.symbol.owner.asClass - def outerArg(receiver: Tree): Tree = receiver match { - case New(_) | Super(_, _) => - singleton(outerPrefix(receiver.tpe)) - case This(_) => - ref(outerParamAccessor(cls)) // will be rewired to outer argument of secondary constructor in phase Constructors - case TypeApply(Select(r, nme.asInstanceOf_), args) => - outerArg(r) // cast was inserted, skip - } - if (hasOuterParam(cls)) - methPart(fun) match { - case Select(receiver, _) => outerArg(receiver).withPos(fun.pos) :: Nil - } - else Nil - } else Nil - } - - /** The path of outer accessors that references `toCls.this` starting from - * the context owner's this node. - */ - def path(toCls: Symbol, start: Tree = This(ctx.owner.enclosingClass.asClass)): Tree = try { - def loop(tree: Tree): Tree = { - val treeCls = tree.tpe.widen.classSymbol - val outerAccessorCtx = ctx.withPhaseNoLater(ctx.lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore - ctx.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)(outerAccessorCtx)} in $treeCls") - if (treeCls == toCls) tree - else { - val acc = outerAccessor(treeCls.asClass)(outerAccessorCtx) - assert(acc.exists, - i"failure to construct path from ${ctx.owner.ownersIterator.toList}%/% to `this` of ${toCls.showLocated};\n${treeCls.showLocated} does not have an outer accessor") - loop(tree.select(acc).ensureApplied) - } - } - ctx.log(i"computing outerpath to $toCls from ${ctx.outersIterator.map(_.owner).toList}") - loop(start) - } catch { - case ex: ClassCastException => - throw new ClassCastException(i"no path exists from ${ctx.owner.enclosingClass} to $toCls") - } - - /** The outer parameter definition of a constructor if it needs one */ - def paramDefs(constr: Symbol): List[ValDef] = - if (constr.isConstructor && hasOuterParam(constr.owner.asClass)) { - val MethodType(outerName :: _, outerType :: _) = constr.info - val outerSym = ctx.newSymbol(constr, outerName, Param, outerType) - ValDef(outerSym) :: Nil - } - else Nil - } -} diff --git a/src/dotty/tools/dotc/transform/ExplicitSelf.scala b/src/dotty/tools/dotc/transform/ExplicitSelf.scala deleted file mode 100644 index 7bb65e575..000000000 --- a/src/dotty/tools/dotc/transform/ExplicitSelf.scala +++ /dev/null @@ -1,47 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Contexts.Context -import Types._ -import TreeTransforms._ -import Decorators._ -import ast.Trees._ -import Flags._ - -/** Transform references of the form - * - * C.this.m - * - * where `C` is a class with explicit self type and `C` is not a - * subclass of the owner of `m` to - * - * C.this.asInstanceOf[S & C.this.type].m - * - * where `S` is the self type of `C`. - * See run/i789.scala for a test case why this is needed. - * - * Also replaces idents referring to the self type with ThisTypes. - */ -class ExplicitSelf extends MiniPhaseTransform { thisTransform => - import ast.tpd._ - - override def phaseName = "explicitSelf" - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = tree.tpe match { - case tp: ThisType => - ctx.debuglog(s"owner = ${ctx.owner}, context = ${ctx}") - This(tp.cls) withPos tree.pos - case _ => tree - } - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { - case Select(thiz: This, name) if name.isTermName => - val cls = thiz.symbol.asClass - val cinfo = cls.classInfo - if (cinfo.givenSelfType.exists && !cls.derivesFrom(tree.symbol.owner)) - cpy.Select(tree)(thiz.asInstance(AndType(cinfo.selfType, thiz.tpe)), name) - else tree - case _ => tree - } -} diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala deleted file mode 100644 index 5ae4e8a54..000000000 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ /dev/null @@ -1,243 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ -package dotty.tools.dotc -package transform - -import dotty.tools.dotc.transform.TreeTransforms._ -import ValueClasses._ -import dotty.tools.dotc.ast.{Trees, tpd} -import scala.collection.{ mutable, immutable } -import mutable.ListBuffer -import core._ -import dotty.tools.dotc.core.Phases.{NeedsCompanions, Phase} -import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ -import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ -import TypeErasure.{ valueErasure, ErasedValueType } -import TypeUtils._ -import util.Positions._ -import Decorators._ -import SymUtils._ - -/** - * Perform Step 1 in the inline classes SIP: Creates extension methods for all - * methods in a value class, except parameter or super accessors, or constructors. - * - * Additionally, for a value class V, let U be the underlying type after erasure. We add - * to the companion module of V two cast methods: - * def u2evt$(x0: U): ErasedValueType(V, U) - * def evt2u$(x0: ErasedValueType(V, U)): U - * The casts are used in [[Erasure]] to make it typecheck, they are then removed - * in [[ElimErasedValueType]]. - * This is different from the implementation of value classes in Scala 2 - * (see SIP-15) which uses `asInstanceOf` which does not typecheck. - * - * Finally, if the constructor of a value class is private pr protected - * it is widened to public. - */ -class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization { thisTransformer => - - import tpd._ - import ExtensionMethods._ - - /** the following two members override abstract members in Transform */ - override def phaseName: String = "extmethods" - - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimRepeated]) - - override def runsAfterGroupsOf = Set(classOf[FirstTransform]) // need companion objects to exist - - override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { - case moduleClassSym: ClassDenotation if moduleClassSym is ModuleClass => - moduleClassSym.linkedClass match { - case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => - val cinfo = moduleClassSym.classInfo - val decls1 = cinfo.decls.cloneScope - val moduleSym = moduleClassSym.symbol.asClass - - var newSuperClass: Type = null - - ctx.atPhase(thisTransformer.next) { implicit ctx => - // In Scala 2, extension methods are added before pickling so we should - // not generate them again. - if (!(valueClass is Scala2x)) ctx.atPhase(thisTransformer) { implicit ctx => - for (decl <- valueClass.classInfo.decls) { - if (isMethodWithExtension(decl)) - decls1.enter(createExtensionMethod(decl, moduleClassSym.symbol)) - } - } - - val underlying = valueErasure(underlyingOfValueClass(valueClass)) - val evt = ErasedValueType(valueClass.typeRef, underlying) - val u2evtSym = ctx.newSymbol(moduleSym, nme.U2EVT, Synthetic | Method, - MethodType(List(nme.x_0), List(underlying), evt)) - val evt2uSym = ctx.newSymbol(moduleSym, nme.EVT2U, Synthetic | Method, - MethodType(List(nme.x_0), List(evt), underlying)) - - val defn = ctx.definitions - - val underlyingCls = underlying.classSymbol - val underlyingClsName = - if (underlyingCls.isNumericValueClass || underlyingCls == defn.BooleanClass) underlyingCls.name - else nme.Object - - val syp = ctx.requiredClass(s"dotty.runtime.vc.VC${underlyingClsName}Companion").asClass - - newSuperClass = tpd.ref(syp).select(nme.CONSTRUCTOR).appliedToType(valueClass.typeRef).tpe.resultType - - decls1.enter(u2evtSym) - decls1.enter(evt2uSym) - } - - // Add the extension methods, the cast methods u2evt$ and evt2u$, and a VC*Companion superclass - moduleClassSym.copySymDenotation(info = - cinfo.derivedClassInfo( - // FIXME: use of VC*Companion superclasses is disabled until the conflicts with SyntheticMethods are solved. - //classParents = ctx.normalizeToClassRefs(List(newSuperClass), moduleSym, decls1), - decls = decls1)) - case _ => - moduleClassSym - } - case ref: SymDenotation => - if (isMethodWithExtension(ref) && ref.hasAnnotation(defn.TailrecAnnot)) { - val ref1 = ref.copySymDenotation() - ref1.removeAnnotation(defn.TailrecAnnot) - ref1 - } - else if (ref.isConstructor && isDerivedValueClass(ref.owner) && ref.is(AccessFlags)) { - val ref1 = ref.copySymDenotation() - ref1.resetFlag(AccessFlags) - ref1 - } - else ref - case _ => - ref - } - - protected def rewiredTarget(target: Symbol, derived: Symbol)(implicit ctx: Context): Symbol = - if (isMethodWithExtension(target) && - target.owner.linkedClass == derived.owner) extensionMethod(target) - else NoSymbol - - private def createExtensionMethod(imeth: Symbol, staticClass: Symbol)(implicit ctx: Context): TermSymbol = { - val extensionName = extensionNames(imeth).head.toTermName - val extensionMeth = ctx.newSymbol(staticClass, extensionName, - imeth.flags | Final &~ (Override | Protected | AbsOverride), - fullyParameterizedType(imeth.info, imeth.owner.asClass), - privateWithin = imeth.privateWithin, coord = imeth.coord) - extensionMeth.addAnnotations(imeth.annotations)(ctx.withPhase(thisTransformer)) - // need to change phase to add tailrec annotation which gets removed from original method in the same phase. - extensionMeth - } - - private val extensionDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() - // TODO: this is state and should be per-run - // todo: check that when transformation finished map is empty - - private def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: ClassSymbol)(implicit ctx: Context): Unit = - if (seen contains clazz) - ctx.error("value class may not unbox to itself", pos) - else { - val unboxed = underlyingOfValueClass(clazz).typeSymbol - if (isDerivedValueClass(unboxed)) checkNonCyclic(pos, seen + clazz, unboxed.asClass) - } - - override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - if (isDerivedValueClass(ctx.owner)) { - /* This is currently redundant since value classes may not - wrap over other value classes anyway. - checkNonCyclic(ctx.owner.pos, Set(), ctx.owner) */ - tree - } else if (ctx.owner.isStaticOwner) { - extensionDefs remove tree.symbol.owner match { - case Some(defns) if defns.nonEmpty => - cpy.Template(tree)(body = tree.body ++ - defns.map(transformFollowing(_))) - case _ => - tree - } - } else tree - } - - override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - if (isMethodWithExtension(tree.symbol)) { - val origMeth = tree.symbol - val origClass = ctx.owner.asClass - val staticClass = origClass.linkedClass - assert(staticClass.exists, s"$origClass lacks companion, ${origClass.owner.definedPeriodsString} ${origClass.owner.info.decls} ${origClass.owner.info.decls}") - val extensionMeth = extensionMethod(origMeth) - ctx.log(s"Value class $origClass spawns extension method.\n Old: ${origMeth.showDcl}\n New: ${extensionMeth.showDcl}") - val store: ListBuffer[Tree] = extensionDefs.get(staticClass) match { - case Some(x) => x - case None => - val newC = new ListBuffer[Tree]() - extensionDefs(staticClass) = newC - newC - } - store += atGroupEnd(fullyParameterizedDef(extensionMeth, tree)(_)) - cpy.DefDef(tree)(rhs = atGroupEnd(forwarder(extensionMeth, tree)(_))) - } else tree - } -} - -object ExtensionMethods { - /** Generate stream of possible names for the extension version of given instance method `imeth`. - * If the method is not overloaded, this stream consists of just "imeth$extension". - * If the method is overloaded, the stream has as first element "imeth$extenionX", where X is the - * index of imeth in the sequence of overloaded alternatives with the same name. This choice will - * always be picked as the name of the generated extension method. - * After this first choice, all other possible indices in the range of 0 until the number - * of overloaded alternatives are returned. The secondary choices are used to find a matching method - * in `extensionMethod` if the first name has the wrong type. We thereby gain a level of insensitivity - * of how overloaded types are ordered between phases and picklings. - */ - private def extensionNames(imeth: Symbol)(implicit ctx: Context): Stream[Name] = { - val decl = imeth.owner.info.decl(imeth.name) - - /** No longer needed for Dotty, as we are more disciplined with scopes now. - // Bridge generation is done at phase `erasure`, but new scopes are only generated - // for the phase after that. So bridges are visible in earlier phases. - // - // `info.member(imeth.name)` filters these out, but we need to use `decl` - // to restrict ourselves to members defined in the current class, so we - // must do the filtering here. - val declTypeNoBridge = decl.filter(sym => !sym.isBridge).tpe - */ - decl match { - case decl: MultiDenotation => - val alts = decl.alternatives - val index = alts indexOf imeth.denot - assert(index >= 0, alts + " does not contain " + imeth) - def altName(index: Int) = (imeth.name + "$extension" + index).toTermName - altName(index) #:: ((0 until alts.length).toStream filter (index != _) map altName) - case decl => - assert(decl.exists, imeth.name + " not found in " + imeth.owner + "'s decls: " + imeth.owner.info.decls) - Stream((imeth.name + "$extension").toTermName) - } - } - - /** Return the extension method that corresponds to given instance method `meth`. */ - def extensionMethod(imeth: Symbol)(implicit ctx: Context): TermSymbol = - ctx.atPhase(ctx.extensionMethodsPhase.next) { implicit ctx => - // FIXME use toStatic instead? - val companionInfo = imeth.owner.companionModule.info - val candidates = extensionNames(imeth) map (companionInfo.decl(_).symbol) filter (_.exists) - val matching = candidates filter (c => FullParameterization.memberSignature(c.info) == imeth.signature) - assert(matching.nonEmpty, - i"""no extension method found for: - | - | $imeth:${imeth.info.show} with signature ${imeth.signature} - | - | Candidates: - | - | ${candidates.map(c => c.name + ":" + c.info.show).mkString("\n")} - | - | Candidates (signatures normalized): - | - | ${candidates.map(c => c.name + ":" + c.info.signature + ":" + FullParameterization.memberSignature(c.info)).mkString("\n")} - | - | Eligible Names: ${extensionNames(imeth).mkString(",")}""") - matching.head.asTerm - } -} diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala deleted file mode 100644 index 597146514..000000000 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ /dev/null @@ -1,193 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Names._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Phases.NeedsCompanions -import dotty.tools.dotc.transform.TreeTransforms._ -import ast.Trees._ -import Flags._ -import Types._ -import Constants.Constant -import Contexts.Context -import Symbols._ -import SymDenotations._ -import Decorators._ -import dotty.tools.dotc.core.Annotations.ConcreteAnnotation -import dotty.tools.dotc.core.Denotations.SingleDenotation -import scala.collection.mutable -import DenotTransformers._ -import typer.Checking -import Names.Name -import NameOps._ -import StdNames._ - - -/** The first tree transform - * - ensures there are companion objects for all classes except module classes - * - eliminates some kinds of trees: Imports, NamedArgs - * - stubs out native methods - * - eliminates self tree in Template and self symbol in ClassInfo - * - collapsess all type trees to trees of class TypeTree - * - converts idempotent expressions with constant types - */ -class FirstTransform extends MiniPhaseTransform with InfoTransformer with AnnotationTransformer { thisTransformer => - import ast.tpd._ - - override def phaseName = "firstTransform" - - private var addCompanionPhases: List[NeedsCompanions] = _ - - def needsCompanion(cls: ClassSymbol)(implicit ctx: Context) = - addCompanionPhases.exists(_.isCompanionNeeded(cls)) - - override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { - addCompanionPhases = ctx.phasePlan.flatMap(_ collect { case p: NeedsCompanions => p }) - this - } - - /** eliminate self symbol in ClassInfo */ - override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match { - case tp @ ClassInfo(_, _, _, _, self: Symbol) => - tp.derivedClassInfo(selfInfo = self.info) - case _ => - tp - } - - /* - tp match { - //create companions for value classes that are not from currently compiled source file - case tp@ClassInfo(_, cls, _, decls, _) - if (ValueClasses.isDerivedValueClass(cls)) && - !sym.isDefinedInCurrentRun && sym.scalacLinkedClass == NoSymbol => - val newDecls = decls.cloneScope - val (modul, mcMethod, symMethod) = newCompanion(sym.name.toTermName, sym) - modul.entered - mcMethod.entered - newDecls.enter(symMethod) - tp.derivedClassInfo(decls = newDecls) - case _ => tp - } - } - */ - - override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { - tree match { - case Select(qual, name) if !name.isOuterSelect && tree.symbol.exists => - assert(qual.tpe derivesFrom tree.symbol.owner, i"non member selection of ${tree.symbol.showLocated} from ${qual.tpe} in $tree") - case _: TypeTree => - case _: Import | _: NamedArg | _: TypTree => - assert(false, i"illegal tree: $tree") - case _ => - } - } - - /** Reorder statements so that module classes always come after their companion classes, add missing companion classes */ - private def reorderAndComplete(stats: List[Tree])(implicit ctx: Context): List[Tree] = { - val moduleClassDefs, singleClassDefs = mutable.Map[Name, Tree]() - - def reorder(stats: List[Tree]): List[Tree] = stats match { - case (stat: TypeDef) :: stats1 if stat.symbol.isClass => - if (stat.symbol is Flags.Module) { - moduleClassDefs += (stat.name -> stat) - singleClassDefs -= stat.name.stripModuleClassSuffix - val stats1r = reorder(stats1) - if (moduleClassDefs contains stat.name) stat :: stats1r else stats1r - } else { - def stats1r = reorder(stats1) - val normalized = moduleClassDefs remove stat.name.moduleClassName match { - case Some(mcdef) => - mcdef :: stats1r - case None => - singleClassDefs += (stat.name -> stat) - stats1r - } - stat :: normalized - } - case stat :: stats1 => stat :: reorder(stats1) - case Nil => Nil - } - - def registerCompanion(name: TermName, forClass: Symbol): TermSymbol = { - val (modul, mcCompanion, classCompanion) = newCompanion(name, forClass) - if (ctx.owner.isClass) modul.enteredAfter(thisTransformer) - mcCompanion.enteredAfter(thisTransformer) - classCompanion.enteredAfter(thisTransformer) - modul - } - - def addMissingCompanions(stats: List[Tree]): List[Tree] = stats map { - case stat: TypeDef if (singleClassDefs contains stat.name) && needsCompanion(stat.symbol.asClass) => - val objName = stat.name.toTermName - val nameClash = stats.exists { - case other: MemberDef => - other.name == objName && other.symbol.info.isParameterless - case _ => - false - } - val uniqueName = if (nameClash) objName.avoidClashName else objName - Thicket(stat :: ModuleDef(registerCompanion(uniqueName, stat.symbol), Nil).trees) - case stat => stat - } - - addMissingCompanions(reorder(stats)) - } - - private def newCompanion(name: TermName, forClass: Symbol)(implicit ctx: Context) = { - val modul = ctx.newCompleteModuleSymbol(forClass.owner, name, Synthetic, Synthetic, - defn.ObjectType :: Nil, Scopes.newScope, assocFile = forClass.asClass.assocFile) - val mc = modul.moduleClass - - val mcComp = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, forClass, mc) - val classComp = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, mc, forClass) - (modul, mcComp, classComp) - } - - /** elimiate self in Template */ - override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { - cpy.Template(impl)(self = EmptyValDef) - } - - override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) = { - if (ddef.symbol.hasAnnotation(defn.NativeAnnot)) { - ddef.symbol.resetFlag(Deferred) - DefDef(ddef.symbol.asTerm, - _ => ref(defn.Sys_errorR).withPos(ddef.pos) - .appliedTo(Literal(Constant("native method stub")))) - } else ddef - } - - override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = - ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisTransformer.next))) - - override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { - case tree: Import => EmptyTree - case tree: NamedArg => transform(tree.arg) - case tree => if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree - } - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = - if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) - else constToLiteral(tree) - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = - if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) - else constToLiteral(tree) - - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) = - constToLiteral(tree) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = - constToLiteral(tree) - - override def transformTyped(tree: Typed)(implicit ctx: Context, info: TransformerInfo) = - constToLiteral(tree) - - override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo) = - constToLiteral(tree) - - // invariants: all modules have companion objects - // all types are TypeTrees - // all this types are explicit -} diff --git a/src/dotty/tools/dotc/transform/Flatten.scala b/src/dotty/tools/dotc/transform/Flatten.scala deleted file mode 100644 index f0104e715..000000000 --- a/src/dotty/tools/dotc/transform/Flatten.scala +++ /dev/null @@ -1,47 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.SymTransformer -import Phases.Phase -import Contexts.Context -import Flags._ -import SymDenotations.SymDenotation -import collection.mutable -import TreeTransforms.MiniPhaseTransform -import dotty.tools.dotc.transform.TreeTransforms.TransformerInfo - -/** Lift nested classes to toplevel */ -class Flatten extends MiniPhaseTransform with SymTransformer { thisTransform => - import ast.tpd._ - override def phaseName = "flatten" - - def transformSym(ref: SymDenotation)(implicit ctx: Context) = { - if (ref.isClass && !ref.is(Package) && !ref.owner.is(Package)) { - ref.copySymDenotation( - name = ref.flatName, - owner = ref.enclosingPackageClass) - } - else ref - } - - private val liftedDefs = new mutable.ListBuffer[Tree] - - private def liftIfNested(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = - if (ctx.owner is Package) tree - else { - transformFollowing(tree).foreachInThicket(liftedDefs += _) - EmptyTree - } - - override def transformStats(stats: List[Tree])(implicit ctx: Context, info: TransformerInfo) = - if (ctx.owner is Package) { - val liftedStats = stats ++ liftedDefs - liftedDefs.clear - liftedStats - } - else stats - - override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = - liftIfNested(tree) -} diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala deleted file mode 100644 index 6c69c735b..000000000 --- a/src/dotty/tools/dotc/transform/FullParameterization.scala +++ /dev/null @@ -1,263 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Types._ -import Contexts._ -import Symbols._ -import Decorators._ -import TypeUtils._ -import StdNames.nme -import NameOps._ -import ast._ -import ast.Trees._ - -import scala.reflect.internal.util.Collections - -/** Provides methods to produce fully parameterized versions of instance methods, - * where the `this` of the enclosing class is abstracted out in an extra leading - * `$this` parameter and type parameters of the class become additional type - * parameters of the fully parameterized method. - * - * Example usage scenarios are: - * - * - extension methods of value classes - * - implementations of trait methods - * - static protected accessors - * - local methods produced by tailrec transform - * - * Note that the methods lift out type parameters of the class containing - * the instance method, but not type parameters of enclosing classes. The - * fully instantiated method therefore needs to be put in a scope "close" - * to the original method, i.e. they need to share the same outer pointer. - * Examples of legal positions are: in the companion object, or as a local - * method inside the original method. - * - * Note: The scheme does not handle yet methods where type parameter bounds - * depend on value parameters of the enclosing class, as in: - * - * class C(val a: String) extends AnyVal { - * def foo[U <: a.type]: Unit = ... - * } - * - * The expansion of method `foo` would lead to - * - * def foo$extension[U <: $this.a.type]($this: C): Unit = ... - * - * which is not typable. Not clear yet what to do. Maybe allow PolyTypes - * to follow method parameters and translate to the following: - * - * def foo$extension($this: C)[U <: $this.a.type]: Unit = ... - * - * @see class-dependent-extension-method.scala in pending/pos. - */ -trait FullParameterization { - - import tpd._ - import FullParameterization._ - - /** If references to original symbol `referenced` from within fully parameterized method - * `derived` should be rewired to some fully parameterized method, the rewiring target symbol, - * otherwise NoSymbol. - */ - protected def rewiredTarget(referenced: Symbol, derived: Symbol)(implicit ctx: Context): Symbol - - /** If references to some original symbol from given tree node within fully parameterized method - * `derived` should be rewired to some fully parameterized method, the rewiring target symbol, - * otherwise NoSymbol. By default implemented as - * - * rewiredTarget(tree.symbol, derived) - * - * but can be overridden. - */ - protected def rewiredTarget(tree: Tree, derived: Symbol)(implicit ctx: Context): Symbol = - rewiredTarget(tree.symbol, derived) - - /** Converts the type `info` of a member of class `clazz` to a method type that - * takes the `this` of the class and any type parameters of the class - * as additional parameters. Example: - * - * class Foo[+A <: AnyRef](val xs: List[A]) extends AnyVal { - * def baz[B >: A](x: B): List[B] = ... - * } - * - * leads to: - * - * object Foo { - * def extension$baz[B >: A <: Any, A >: Nothing <: AnyRef]($this: Foo[A])(x: B): List[B] - * } - * - * If a self type is present, $this has this self type as its type. - * - * @param abstractOverClass if true, include the type parameters of the class in the method's list of type parameters. - * @param liftThisType if true, require created $this to be $this: (Foo[A] & Foo,this). - * This is needed if created member stays inside scope of Foo(as in tailrec) - */ - def fullyParameterizedType(info: Type, clazz: ClassSymbol, abstractOverClass: Boolean = true, liftThisType: Boolean = false)(implicit ctx: Context): Type = { - val (mtparamCount, origResult) = info match { - case info: PolyType => (info.paramNames.length, info.resultType) - case info: ExprType => (0, info.resultType) - case _ => (0, info) - } - val ctparams = if (abstractOverClass) clazz.typeParams else Nil - val ctnames = ctparams.map(_.name.unexpandedName) - - /** The method result type */ - def resultType(mapClassParams: Type => Type) = { - val thisParamType = mapClassParams(clazz.classInfo.selfType) - val firstArgType = if (liftThisType) thisParamType & clazz.thisType else thisParamType - MethodType(nme.SELF :: Nil, firstArgType :: Nil)(mt => - mapClassParams(origResult).substThisUnlessStatic(clazz, MethodParam(mt, 0))) - } - - /** Replace class type parameters by the added type parameters of the polytype `pt` */ - def mapClassParams(tp: Type, pt: PolyType): Type = { - val classParamsRange = (mtparamCount until mtparamCount + ctparams.length).toList - tp.substDealias(ctparams, classParamsRange map (PolyParam(pt, _))) - } - - /** The bounds for the added type parameters of the polytype `pt` */ - def mappedClassBounds(pt: PolyType): List[TypeBounds] = - ctparams.map(tparam => mapClassParams(tparam.info, pt).bounds) - - info match { - case info: PolyType => - PolyType(info.paramNames ++ ctnames)( - pt => - (info.paramBounds.map(mapClassParams(_, pt).bounds) ++ - mappedClassBounds(pt)).mapConserve(_.subst(info, pt).bounds), - pt => resultType(mapClassParams(_, pt)).subst(info, pt)) - case _ => - if (ctparams.isEmpty) resultType(identity) - else PolyType(ctnames)(mappedClassBounds, pt => resultType(mapClassParams(_, pt))) - } - } - - /** The type parameters (skolems) of the method definition `originalDef`, - * followed by the class parameters of its enclosing class. - */ - private def allInstanceTypeParams(originalDef: DefDef, abstractOverClass: Boolean)(implicit ctx: Context): List[Symbol] = - if (abstractOverClass) - originalDef.tparams.map(_.symbol) ::: originalDef.symbol.enclosingClass.typeParams - else originalDef.tparams.map(_.symbol) - - /** Given an instance method definition `originalDef`, return a - * fully parameterized method definition derived from `originalDef`, which - * has `derived` as symbol and `fullyParameterizedType(originalDef.symbol.info)` - * as info. - * `abstractOverClass` defines weather the DefDef should abstract over type parameters - * of class that contained original defDef - */ - def fullyParameterizedDef(derived: TermSymbol, originalDef: DefDef, abstractOverClass: Boolean = true)(implicit ctx: Context): Tree = - polyDefDef(derived, trefs => vrefss => { - val origMeth = originalDef.symbol - val origClass = origMeth.enclosingClass.asClass - val origTParams = allInstanceTypeParams(originalDef, abstractOverClass) - val origVParams = originalDef.vparamss.flatten map (_.symbol) - val thisRef :: argRefs = vrefss.flatten - - /** If tree should be rewired, the rewired tree, otherwise EmptyTree. - * @param targs Any type arguments passed to the rewired tree. - */ - def rewireTree(tree: Tree, targs: List[Tree])(implicit ctx: Context): Tree = { - def rewireCall(thisArg: Tree): Tree = { - val rewired = rewiredTarget(tree, derived) - if (rewired.exists) { - val base = thisArg.tpe.baseTypeWithArgs(origClass) - assert(base.exists) - ref(rewired.termRef) - .appliedToTypeTrees(targs ++ base.argInfos.map(TypeTree(_))) - .appliedTo(thisArg) - } else EmptyTree - } - tree match { - case Return(expr, from) if !from.isEmpty => - val rewired = rewiredTarget(from, derived) - if (rewired.exists) - tpd.cpy.Return(tree)(expr, Ident(rewired.termRef)) - else - EmptyTree - case Ident(_) => rewireCall(thisRef) - case Select(qual, _) => rewireCall(qual) - case tree @ TypeApply(fn, targs1) => - assert(targs.isEmpty) - rewireTree(fn, targs1) - case _ => EmptyTree - } - } - - /** Type rewiring is needed because a previous reference to an instance - * method might still persist in the types of enclosing nodes. Example: - * - * if (true) this.imeth else this.imeth - * - * is rewritten to - * - * if (true) xmeth($this) else xmeth($this) - * - * but the type `this.imeth` still persists as the result type of the `if`, - * because it is kept by the `cpy` operation of the tree transformer. - * It needs to be rewritten to the common result type of `imeth` and `xmeth`. - */ - def rewireType(tpe: Type) = tpe match { - case tpe: TermRef if rewiredTarget(tpe.symbol, derived).exists => tpe.widen - case _ => tpe - } - - new TreeTypeMap( - typeMap = rewireType(_) - .substDealias(origTParams, trefs) - .subst(origVParams, argRefs.map(_.tpe)) - .substThisUnlessStatic(origClass, thisRef.tpe), - treeMap = { - case tree: This if tree.symbol == origClass => thisRef - case tree => rewireTree(tree, Nil) orElse tree - }, - oldOwners = origMeth :: Nil, - newOwners = derived :: Nil - ).transform(originalDef.rhs) - }) - - /** A forwarder expression which calls `derived`, passing along - * - if `abstractOverClass` the type parameters and enclosing class parameters of originalDef`, - * - the `this` of the enclosing class, - * - the value parameters of the original method `originalDef`. - */ - def forwarder(derived: TermSymbol, originalDef: DefDef, abstractOverClass: Boolean = true, liftThisType: Boolean = false)(implicit ctx: Context): Tree = { - val fun = - ref(derived.termRef) - .appliedToTypes(allInstanceTypeParams(originalDef, abstractOverClass).map(_.typeRef)) - .appliedTo(This(originalDef.symbol.enclosingClass.asClass)) - - (if (!liftThisType) - fun.appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol))) - else { - // this type could have changed on forwarding. Need to insert a cast. - val args = Collections.map2(originalDef.vparamss, fun.tpe.paramTypess)((vparams, paramTypes) => - Collections.map2(vparams, paramTypes)((vparam, paramType) => { - assert(vparam.tpe <:< paramType.widen) // type should still conform to widened type - ref(vparam.symbol).ensureConforms(paramType) - }) - ) - fun.appliedToArgss(args) - - }).withPos(originalDef.rhs.pos) - } -} - -object FullParameterization { - - /** Assuming `info` is a result of a `fullyParameterizedType` call, the signature of the - * original method type `X` such that `info = fullyParameterizedType(X, ...)`. - */ - def memberSignature(info: Type)(implicit ctx: Context): Signature = info match { - case info: PolyType => - memberSignature(info.resultType) - case info @ MethodType(nme.SELF :: Nil, _) => - info.resultType.ensureMethodic.signature - case info @ MethodType(nme.SELF :: otherNames, thisType :: otherTypes) => - info.derivedMethodType(otherNames, otherTypes, info.resultType).signature - case _ => - Signature.NotAMethod - } -} diff --git a/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala b/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala deleted file mode 100644 index 5fd89314a..000000000 --- a/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala +++ /dev/null @@ -1,83 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import core.SymDenotations._ -import core.StdNames.nme -import core.Names._ -import core.NameOps._ -import ast.Trees._ -import SymUtils._ -import dotty.tools.dotc.ast.tpd -import collection.{ mutable, immutable } -import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet } - -/** - * Rewires closures to implement more specific types of Functions. - */ -class FunctionalInterfaces extends MiniPhaseTransform { - import tpd._ - - def phaseName: String = "functionalInterfaces" - - private var allowedReturnTypes: Set[Symbol] = _ // moved here to make it explicit what specializations are generated - private var allowedArgumentTypes: Set[Symbol] = _ - val maxArgsCount = 2 - - def shouldSpecialize(m: MethodType)(implicit ctx: Context) = - (m.paramTypes.size <= maxArgsCount) && - m.paramTypes.forall(x => allowedArgumentTypes.contains(x.typeSymbol)) && - allowedReturnTypes.contains(m.resultType.typeSymbol) - - val functionName = "JFunction".toTermName - val functionPackage = "scala.compat.java8.".toTermName - - override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { - allowedReturnTypes = Set(defn.UnitClass, - defn.BooleanClass, - defn.IntClass, - defn.FloatClass, - defn.LongClass, - defn.DoubleClass, - /* only for Function0: */ defn.ByteClass, - defn.ShortClass, - defn.CharClass) - - allowedArgumentTypes = Set(defn.IntClass, - defn.LongClass, - defn.DoubleClass, - /* only for Function1: */ defn.FloatClass) - - this - } - - override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo): Tree = { - tree.tpt match { - case EmptyTree => - val m = tree.meth.tpe.widen.asInstanceOf[MethodType] - - if (shouldSpecialize(m)) { - val functionSymbol = tree.tpe.widenDealias.classSymbol - val names = ctx.atPhase(ctx.erasurePhase) { - implicit ctx => functionSymbol.typeParams.map(_.name) - } - val interfaceName = (functionName ++ m.paramTypes.length.toString).specializedFor(m.paramTypes ::: m.resultType :: Nil, names, Nil, Nil) - - // symbols loaded from classpath aren't defined in periods earlier than when they where loaded - val interface = ctx.withPhase(ctx.typerPhase).getClassIfDefined(functionPackage ++ interfaceName) - if (interface.exists) { - val tpt = tpd.TypeTree(interface.asType.typeRef) - tpd.Closure(tree.env, tree.meth, tpt) - } else tree - } else tree - case _ => - tree - } - } -} diff --git a/src/dotty/tools/dotc/transform/GetClass.scala b/src/dotty/tools/dotc/transform/GetClass.scala deleted file mode 100644 index 6a9a5fda2..000000000 --- a/src/dotty/tools/dotc/transform/GetClass.scala +++ /dev/null @@ -1,34 +0,0 @@ -package dotty.tools.dotc -package transform - -import ast.tpd -import core.Contexts.Context -import core.StdNames.nme -import core.Phases.Phase -import TypeUtils._ -import TreeTransforms.{MiniPhaseTransform, TransformerInfo} - -/** Rewrite `getClass` calls as follow: - * - * For every instance of primitive class C whose boxed class is called B: - * instanceC.getClass -> B.TYPE - * For every instance of non-primitive class D: - * instanceD.getClass -> instanceD.getClass - */ -class GetClass extends MiniPhaseTransform { - import tpd._ - - override def phaseName: String = "getClass" - - // getClass transformation should be applied to specialized methods - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure], classOf[FunctionalInterfaces]) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { - import ast.Trees._ - tree match { - case Apply(Select(qual, nme.getClass_), Nil) if qual.tpe.widen.isPrimitiveValueType => - clsOf(qual.tpe.widen).withPos(tree.pos) - case _ => tree - } - } -} diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala deleted file mode 100644 index 31171dfab..000000000 --- a/src/dotty/tools/dotc/transform/Getters.scala +++ /dev/null @@ -1,76 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.SymTransformer -import Contexts.Context -import SymDenotations.SymDenotation -import Types._ -import Symbols._ -import SymUtils._ -import Constants._ -import TreeTransforms._ -import Flags._ -import Decorators._ -import ValueClasses._ - -/** Performs the following rewritings for fields of a class: - * - * <mods> val x: T = e - * --> <mods> <stable> <accessor> def x: T = e - * <mods> var x: T = e - * --> <mods> <accessor> def x: T = e - * - * <mods> val x: T - * --> <mods> <stable> <accessor> def x: T - * - * <mods> lazy val x: T = e - * --> <mods> <accessor> lazy def x: T =e - * - * <mods> var x: T - * --> <mods> <accessor> def x: T - * - * <mods> non-static <module> val x$ = e - * --> <mods> <module> <accessor> def x$ = e - * - * Omitted from the rewritings are - * - * - private[this] fields in classes (excluding traits, value classes) - * - fields generated for static modules (TODO: needed?) - * - parameters, static fields, and fields coming from Java - * - * Furthermore, assignments to mutable vars are replaced by setter calls - * - * p.x = e - * --> p.x_=(e) - * - * No fields are generated yet. This is done later in phase Memoize. - */ -class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => - import ast.tpd._ - - override def phaseName = "getters" - - override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { - def noGetterNeeded = - d.is(NoGetterNeeded) || - d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) && !isDerivedValueClass(d.owner) && !d.is(Flags.Lazy) || - d.is(Module) && d.isStatic || - d.hasAnnotation(defn.ScalaStaticAnnot) || - d.isSelfSym - if (d.isTerm && (d.is(Lazy) || d.owner.isClass) && d.info.isValueType && !noGetterNeeded) { - val maybeStable = if (d.isStable) Stable else EmptyFlags - d.copySymDenotation( - initFlags = d.flags | maybeStable | AccessorCreationFlags, - info = ExprType(d.info)) - } - else d - } - private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic - - override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = - if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs).withPos(tree.pos) else tree - - override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = - if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs).withPos(tree.pos) else tree -} diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala deleted file mode 100644 index 7c60e8d72..000000000 --- a/src/dotty/tools/dotc/transform/InterceptedMethods.scala +++ /dev/null @@ -1,131 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.Denotations._ -import core.SymDenotations._ -import core.Contexts._ -import core.Types._ -import ast.Trees._ -import ast.tpd.{Apply, Tree, cpy} -import dotty.tools.dotc.ast.tpd -import scala.collection.mutable -import dotty.tools.dotc._ -import core._ -import Contexts._ -import Symbols._ -import Decorators._ -import NameOps._ -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, TreeTransform} -import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.ast.{untpd, tpd} -import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.core.Types.MethodType -import dotty.tools.dotc.core.Names.Name -import scala.collection.mutable.ListBuffer -import dotty.tools.dotc.core.Denotations.SingleDenotation -import dotty.tools.dotc.core.SymDenotations.SymDenotation -import StdNames._ -import Phases.Phase - -/** Replace member references as follows: - * - * - `x != y` for != in class Any becomes `!(x == y)` with == in class Any. - * - `x.##` for ## in NullClass becomes `0` - * - `x.##` for ## in Any becomes calls to ScalaRunTime.hash, - * using the most precise overload available - * - `x.getClass` for getClass in primitives becomes `x.getClass` with getClass in class Object. - */ -class InterceptedMethods extends MiniPhaseTransform { - thisTransform => - - import tpd._ - - override def phaseName: String = "intercepted" - - private var primitiveGetClassMethods: Set[Symbol] = _ - - var Any_## : Symbol = _ // cached for performance reason - - /** perform context-dependant initialization */ - override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { - this.Any_## = defn.Any_## - primitiveGetClassMethods = Set[Symbol]() ++ defn.ScalaValueClasses().map(x => x.requiredMethod(nme.getClass_)) - this - } - - // this should be removed if we have guarantee that ## will get Apply node - override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (tree.symbol.isTerm && (Any_## eq tree.symbol.asTerm)) { - val rewrite = poundPoundValue(tree.qualifier) - ctx.log(s"$phaseName rewrote $tree to $rewrite") - rewrite - } - else tree - } - - private def poundPoundValue(tree: Tree)(implicit ctx: Context) = { - val s = tree.tpe.widen.typeSymbol - if (s == defn.NullClass) Literal(Constant(0)) - else { - // Since we are past typer, we need to avoid creating trees carrying - // overloaded types. This logic is custom (and technically incomplete, - // although serviceable) for def hash. What is really needed is for - // the overloading logic presently hidden away in a few different - // places to be properly exposed so we can just call "resolveOverload" - // after typer. Until then: - - def alts = defn.ScalaRuntimeModule.info.member(nme.hash_) - - // if tpe is a primitive value type, alt1 will match on the exact value, - // taking in account that null.asInstanceOf[Int] == 0 - def alt1 = alts.suchThat(_.info.firstParamTypes.head =:= tree.tpe.widen) - - // otherwise alt2 will match. alt2 also knows how to handle 'null' runtime value - def alt2 = defn.ScalaRuntimeModule.info.member(nme.hash_) - .suchThat(_.info.firstParamTypes.head.typeSymbol == defn.AnyClass) - - Ident((if (s.isNumericValueClass) alt1 else alt2).termRef) - .appliedTo(tree) - } - } - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { - def unknown = { - assert(false, s"The symbol '${tree.fun.symbol.showLocated}' was intercepted but didn't match any cases, " + - s"that means the intercepted methods set doesn't match the code") - tree - } - lazy val Select(qual, _) = tree.fun - val Any_## = this.Any_## - val Any_!= = defn.Any_!= - val rewrite: Tree = tree.fun.symbol match { - case Any_## => - poundPoundValue(qual) - case Any_!= => - qual.select(defn.Any_==).appliedToArgs(tree.args).select(defn.Boolean_!) - /* - /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { - // todo: this is needed to support value classes - // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) - global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, - List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen)))) - }*/ - */ - case t if primitiveGetClassMethods.contains(t) => - // if we got here then we're trying to send a primitive getClass method to either - // a) an Any, in which cage Object_getClass works because Any erases to object. Or - // - // b) a non-primitive, e.g. because the qualifier's type is a refinement type where one parent - // of the refinement is a primitive and another is AnyRef. In that case - // we get a primitive form of _getClass trying to target a boxed value - // so we need replace that method name with Object_getClass to get correct behavior. - // See SI-5568. - qual.selectWithSig(defn.Any_getClass).appliedToNone - case _ => - tree - } - ctx.log(s"$phaseName rewrote $tree to $rewrite") - rewrite - } -} diff --git a/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala b/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala deleted file mode 100644 index 8bc4a2aa9..000000000 --- a/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala +++ /dev/null @@ -1,168 +0,0 @@ -package dotty.tools.dotc -package transform - -import dotty.tools.dotc.util.Positions._ -import TreeTransforms.{MiniPhaseTransform, TransformerInfo} -import core._ -import Contexts.Context, Types._, Constants._, Decorators._, Symbols._ -import TypeUtils._, TypeErasure._, Flags._ - - -/** Implements partial evaluation of `sc.isInstanceOf[Sel]` according to: - * - * +-------------+----------------------------+----------------------------+------------------+ - * | Sel\sc | trait | class | final class | - * +-------------+----------------------------+----------------------------+------------------+ - * | trait | ? | ? | statically known | - * | class | ? | false if classes unrelated | statically known | - * | final class | false if classes unrelated | false if classes unrelated | statically known | - * +-------------+----------------------------+----------------------------+------------------+ - * - * This is a generalized solution to raising an error on unreachable match - * cases and warnings on other statically known results of `isInstanceOf`. - * - * Steps taken: - * - * 1. evalTypeApply will establish the matrix and choose the appropriate - * handling for the case: - * 2. a) Sel/sc is a value class or scrutinee is `Any` - * b) handleStaticallyKnown - * c) falseIfUnrelated with `scrutinee <:< selector` - * d) handleFalseUnrelated - * e) leave as is (aka `happens`) - * 3. Rewrite according to step taken in `2` - */ -class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer => - - import dotty.tools.dotc.ast.tpd._ - - def phaseName = "isInstanceOfEvaluator" - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { - val defn = ctx.definitions - - /** Handles the four cases of statically known `isInstanceOf`s and gives - * the correct warnings, or an error if statically known to be false in - * match - */ - def handleStaticallyKnown(select: Select, scrutinee: Type, selector: Type, inMatch: Boolean, pos: Position): Tree = { - val scrutineeSubSelector = scrutinee <:< selector - if (!scrutineeSubSelector && inMatch) { - ctx.error( - s"this case is unreachable due to `${selector.show}` not being a subclass of `${scrutinee.show}`", - Position(pos.start - 5, pos.end - 5) - ) - rewrite(select, to = false) - } else if (!scrutineeSubSelector && !inMatch) { - ctx.warning( - s"this will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}` (will be optimized away)", - pos - ) - rewrite(select, to = false) - } else if (scrutineeSubSelector && !inMatch) { - ctx.warning( - s"this will always yield true if the scrutinee is non-null, since `${scrutinee.show}` is a subclass of `${selector.show}` (will be optimized away)", - pos - ) - rewrite(select, to = true) - } else /* if (scrutineeSubSelector && inMatch) */ rewrite(select, to = true) - } - - /** Rewrites cases with unrelated types */ - def handleFalseUnrelated(select: Select, scrutinee: Type, selector: Type, inMatch: Boolean) = - if (inMatch) { - ctx.error( - s"will never match since `${selector.show}` is not a subclass of `${scrutinee.show}`", - Position(select.pos.start - 5, select.pos.end - 5) - ) - rewrite(select, to = false) - } else { - ctx.warning( - s"will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}`", - select.pos - ) - rewrite(select, to = false) - } - - /** Rewrites the select to a boolean if `to` is false or if the qualifier - * is a value class. - * - * If `to` is set to true and the qualifier is not a primitive, the - * instanceOf is replaced by a null check, since: - * - * `scrutinee.isInstanceOf[Selector]` if `scrutinee eq null` - */ - def rewrite(tree: Select, to: Boolean): Tree = - if (!to || !tree.qualifier.tpe.widen.derivesFrom(defn.AnyRefAlias)) { - val literal = Literal(Constant(to)) - if (!isPureExpr(tree.qualifier)) Block(List(tree.qualifier), literal) - else literal - } else - Apply(tree.qualifier.select(defn.Object_ne), List(Literal(Constant(null)))) - - /** Attempts to rewrite TypeApply to either `scrutinee ne null` or a - * constant - */ - def evalTypeApply(tree: TypeApply): Tree = - if (tree.symbol != defn.Any_isInstanceOf) tree - else tree.fun match { - case s: Select => { - val scrutinee = erasure(s.qualifier.tpe.widen) - val selector = erasure(tree.args.head.tpe.widen) - - val scTrait = scrutinee.typeSymbol is Trait - val scClass = - scrutinee.typeSymbol.isClass && - !(scrutinee.typeSymbol is Trait) && - !(scrutinee.typeSymbol is Module) - - val scClassNonFinal = scClass && !(scrutinee.typeSymbol is Final) - val scFinalClass = scClass && (scrutinee.typeSymbol is Final) - - val selTrait = selector.typeSymbol is Trait - val selClass = - selector.typeSymbol.isClass && - !(selector.typeSymbol is Trait) && - !(selector.typeSymbol is Module) - - val selClassNonFinal = selClass && !(selector.typeSymbol is Final) - val selFinalClass = selClass && (selector.typeSymbol is Final) - - // Cases --------------------------------- - val valueClassesOrAny = - ValueClasses.isDerivedValueClass(scrutinee.typeSymbol) || - ValueClasses.isDerivedValueClass(selector.typeSymbol) || - scrutinee == defn.ObjectType - - val knownStatically = scFinalClass - - val falseIfUnrelated = - (scClassNonFinal && selClassNonFinal) || - (scClassNonFinal && selFinalClass) || - (scTrait && selFinalClass) - - val happens = - (scClassNonFinal && selClassNonFinal) || - (scTrait && selClassNonFinal) || - (scTrait && selTrait) - - val inMatch = s.qualifier.symbol is Case - - if (valueClassesOrAny) tree - else if (knownStatically) - handleStaticallyKnown(s, scrutinee, selector, inMatch, tree.pos) - else if (falseIfUnrelated && scrutinee <:< selector) - // scrutinee is a subtype of the selector, safe to rewrite - rewrite(s, to = true) - else if (falseIfUnrelated && !(selector <:< scrutinee)) - // selector and scrutinee are unrelated - handleFalseUnrelated(s, scrutinee, selector, inMatch) - else if (happens) tree - else tree - } - - case _ => tree - } - - evalTypeApply(tree) - } -} diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala deleted file mode 100644 index 19fb3dd0c..000000000 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ /dev/null @@ -1,548 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import core.StdNames.nme -import core.Names._ -import core.NameOps._ -import core.Phases._ -import ast.Trees._ -import SymUtils._ -import ExplicitOuter.outer -import util.Attachment -import util.NameTransformer -import util.Positions._ -import collection.{ mutable, immutable } -import collection.mutable.{ HashMap, HashSet, LinkedHashMap, LinkedHashSet, TreeSet } - -object LambdaLift { - private val NJ = NameTransformer.NAME_JOIN_STRING - private class NoPath extends Exception -} - -/** This phase performs the necessary rewritings to eliminate classes and methods - * nested in other methods. In detail: - * 1. It adds all free variables of local functions as additional parameters (proxies). - * 2. It rebinds references to free variables to the corresponding proxies, - * 3. It lifts all local functions and classes out as far as possible, but at least - * to the enclosing class. - * 4. It stores free variables of non-trait classes as additional fields of the class. - * The fields serve as proxies for methods in the class, which avoids the need - * of passing additional parameters to these methods. - * - * A particularly tricky case are local traits. These cannot store free variables - * as field proxies, because LambdaLift runs after Mixin, so the fields cannot be - * expanded anymore. Instead, methods of local traits get free variables of - * the trait as additional proxy parameters. The difference between local classes - * and local traits is illustrated by the two rewritings below. - * - * def f(x: Int) = { def f(x: Int) = new C(x).f2 - * class C { ==> class C(x$1: Int) { - * def f2 = x def f2 = x$1 - * } } - * new C().f2 - * } - * - * def f(x: Int) = { def f(x: Int) = new C().f2(x) - * trait T { ==> trait T - * def f2 = x def f2(x$1: Int) = x$1 - * } } - * class C extends T class C extends T - * new C().f2 - * } - */ -class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform => - import LambdaLift._ - import ast.tpd._ - - /** the following two members override abstract members in Transform */ - val phaseName: String = "lambdaLift" - val treeTransform = new LambdaLifter - - override def relaxedTyping = true - - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors]) - // Constructors has to happen before LambdaLift because the lambda lift logic - // becomes simpler if it can assume that parameter accessors have already been - // converted to parameters in super calls. Without this it is very hard to get - // lambda lift for super calls right. Witness the implementation restrictions to - // this effect in scalac. - - class LambdaLifter extends TreeTransform { - override def phase = thisTransform - - private type SymSet = TreeSet[Symbol] - - /** A map storing free variables of functions and classes */ - private val free = new LinkedHashMap[Symbol, SymSet] - - /** A map storing the free variable proxies of functions and classes. - * For every function and class, this is a map from the free variables - * of that function or class to the proxy symbols accessing them. - */ - private val proxyMap = new LinkedHashMap[Symbol, Map[Symbol, Symbol]] - - /** A hashtable storing calls between functions */ - private val called = new LinkedHashMap[Symbol, SymSet] - - /** Symbols that are called from an inner class. */ - private val calledFromInner = new HashSet[Symbol] - - /** A map from local methods and classes to the owners to which they will be lifted as members. - * For methods and classes that do not have any dependencies this will be the enclosing package. - * symbols with packages as lifted owners will subsequently represented as static - * members of their toplevel class, unless their enclosing class was already static. - * Note: During tree transform (which runs at phase LambdaLift + 1), liftedOwner - * is also used to decide whether a method had a term owner before. - */ - private val liftedOwner = new HashMap[Symbol, Symbol] - - /** The outer parameter of a constructor */ - private val outerParam = new HashMap[Symbol, Symbol] - - /** Buffers for lifted out classes and methods, indexed by owner */ - private val liftedDefs = new HashMap[Symbol, mutable.ListBuffer[Tree]] - - /** A flag to indicate whether new free variables have been found */ - private var changedFreeVars: Boolean = _ - - /** A flag to indicate whether lifted owners have changed */ - private var changedLiftedOwner: Boolean = _ - - private val ord: Ordering[Symbol] = Ordering.by((_: Symbol).id) // Dotty deviation: Type annotation needed. TODO: figure out why - private def newSymSet = TreeSet.empty[Symbol](ord) - - private def symSet(f: LinkedHashMap[Symbol, SymSet], sym: Symbol): SymSet = - f.getOrElseUpdate(sym, newSymSet) - - def freeVars(sym: Symbol): List[Symbol] = free get sym match { - case Some(set) => set.toList - case None => Nil - } - - def proxyOf(sym: Symbol, fv: Symbol) = proxyMap.getOrElse(sym, Map.empty)(fv) - - def proxies(sym: Symbol): List[Symbol] = freeVars(sym).map(proxyOf(sym, _)) - - /** A symbol is local if it is owned by a term or a local trait, - * or if it is a constructor of a local symbol. - */ - def isLocal(sym: Symbol)(implicit ctx: Context): Boolean = { - val owner = sym.maybeOwner - owner.isTerm || - owner.is(Trait) && isLocal(owner) || - sym.isConstructor && isLocal(owner) - } - - /** Set `liftedOwner(sym)` to `owner` if `owner` is more deeply nested - * than the previous value of `liftedowner(sym)`. - */ - def narrowLiftedOwner(sym: Symbol, owner: Symbol)(implicit ctx: Context) = - if (sym.maybeOwner.isTerm && - owner.isProperlyContainedIn(liftedOwner(sym)) && - owner != sym) { - ctx.log(i"narrow lifted $sym to $owner") - changedLiftedOwner = true - liftedOwner(sym) = owner - } - - /** Mark symbol `sym` as being free in `enclosure`, unless `sym` is defined - * in `enclosure` or there is an intermediate class properly containing `enclosure` - * in which `sym` is also free. Also, update `liftedOwner` of `enclosure` so - * that `enclosure` can access `sym`, or its proxy in an intermediate class. - * This means: - * - * 1. If there is an intermediate class in which `sym` is free, `enclosure` - * must be contained in that class (in order to access the `sym proxy stored - * in the class). - * - * 2. If there is no intermediate class, `enclosure` must be contained - * in the class enclosing `sym`. - * - * @return If there is a non-trait class between `enclosure` and - * the owner of `sym`, the largest such class. - * Otherwise, if there is a trait between `enclosure` and - * the owner of `sym`, the largest such trait. - * Otherwise, NoSymbol. - * - * @pre sym.owner.isTerm, (enclosure.isMethod || enclosure.isClass) - * - * The idea of `markFree` is illustrated with an example: - * - * def f(x: int) = { - * class C { - * class D { - * val y = x - * } - * } - * } - * - * In this case `x` is free in the primary constructor of class `C`. - * but it is not free in `D`, because after lambda lift the code would be transformed - * as follows: - * - * def f(x$0: int) { - * class C(x$0: int) { - * val x$1 = x$0 - * class D { - * val y = outer.x$1 - * } - * } - * } - */ - private def markFree(sym: Symbol, enclosure: Symbol)(implicit ctx: Context): Symbol = try { - if (!enclosure.exists) throw new NoPath - if (enclosure == sym.enclosure) NoSymbol - else { - ctx.debuglog(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure") - val intermediate = - if (enclosure.is(PackageClass)) enclosure - else markFree(sym, enclosure.enclosure) - narrowLiftedOwner(enclosure, intermediate orElse sym.enclosingClass) - if (!intermediate.isRealClass || enclosure.isConstructor) { - // Constructors and methods nested inside traits get the free variables - // of the enclosing trait or class. - // Conversely, local traits do not get free variables. - if (!enclosure.is(Trait)) - if (symSet(free, enclosure).add(sym)) { - changedFreeVars = true - ctx.log(i"$sym is free in $enclosure") - } - } - if (intermediate.isRealClass) intermediate - else if (enclosure.isRealClass) enclosure - else if (intermediate.isClass) intermediate - else if (enclosure.isClass) enclosure - else NoSymbol - } - } catch { - case ex: NoPath => - println(i"error lambda lifting ${ctx.compilationUnit}: $sym is not visible from $enclosure") - throw ex - } - - private def markCalled(callee: Symbol, caller: Symbol)(implicit ctx: Context): Unit = { - ctx.debuglog(i"mark called: $callee of ${callee.owner} is called by $caller in ${caller.owner}") - assert(isLocal(callee)) - symSet(called, caller) += callee - if (callee.enclosingClass != caller.enclosingClass) calledFromInner += callee - } - - private class CollectDependencies extends EnclosingMethodTraverser { - def traverse(enclosure: Symbol, tree: Tree)(implicit ctx: Context) = try { //debug - val sym = tree.symbol - def narrowTo(thisClass: ClassSymbol) = { - val enclClass = enclosure.enclosingClass - narrowLiftedOwner(enclosure, - if (enclClass.isContainedIn(thisClass)) thisClass - else enclClass) // unknown this reference, play it safe and assume the narrowest possible owner - } - tree match { - case tree: Ident => - if (isLocal(sym)) { - if (sym is Label) - assert(enclosure == sym.enclosure, - i"attempt to refer to label $sym from nested $enclosure") - else if (sym is Method) markCalled(sym, enclosure) - else if (sym.isTerm) markFree(sym, enclosure) - } - def captureImplicitThis(x: Type): Unit = { - x match { - case tr@TermRef(x, _) if (!tr.termSymbol.isStatic) => captureImplicitThis(x) - case x: ThisType if (!x.tref.typeSymbol.isStaticOwner) => narrowTo(x.tref.typeSymbol.asClass) - case _ => - } - } - captureImplicitThis(tree.tpe) - case tree: Select => - if (sym.is(Method) && isLocal(sym)) markCalled(sym, enclosure) - case tree: This => - narrowTo(tree.symbol.asClass) - case tree: DefDef => - if (sym.owner.isTerm && !sym.is(Label)) - liftedOwner(sym) = sym.enclosingPackageClass - // this will make methods in supercall constructors of top-level classes owned - // by the enclosing package, which means they will be static. - // On the other hand, all other methods will be indirectly owned by their - // top-level class. This avoids possible deadlocks when a static method - // has to access its enclosing object from the outside. - else if (sym.isConstructor) { - if (sym.isPrimaryConstructor && isLocal(sym.owner) && !sym.owner.is(Trait)) - // add a call edge from the constructor of a local non-trait class to - // the class itself. This is done so that the constructor inherits - // the free variables of the class. - symSet(called, sym) += sym.owner - - tree.vparamss.head.find(_.name == nme.OUTER) match { - case Some(vdef) => outerParam(sym) = vdef.symbol - case _ => - } - } - case tree: TypeDef => - if (sym.owner.isTerm) liftedOwner(sym) = sym.topLevelClass.owner - case tree: Template => - liftedDefs(tree.symbol.owner) = new mutable.ListBuffer - case _ => - } - foldOver(enclosure, tree) - } catch { //debug - case ex: Exception => - println(i"$ex while traversing $tree") - throw ex - } - } - - /** Compute final free variables map `fvs by closing over caller dependencies. */ - private def computeFreeVars()(implicit ctx: Context): Unit = - do { - changedFreeVars = false - for { - caller <- called.keys - callee <- called(caller) - fvs <- free get callee - fv <- fvs - } markFree(fv, caller) - } while (changedFreeVars) - - /** Compute final liftedOwner map by closing over caller dependencies */ - private def computeLiftedOwners()(implicit ctx: Context): Unit = - do { - changedLiftedOwner = false - for { - caller <- called.keys - callee <- called(caller) - } { - val normalizedCallee = callee.skipConstructor - val calleeOwner = normalizedCallee.owner - if (calleeOwner.isTerm) narrowLiftedOwner(caller, liftedOwner(normalizedCallee)) - else { - assert(calleeOwner.is(Trait)) - // methods nested inside local trait methods cannot be lifted out - // beyond the trait. Note that we can also call a trait method through - // a qualifier; in that case no restriction to lifted owner arises. - if (caller.isContainedIn(calleeOwner)) - narrowLiftedOwner(caller, calleeOwner) - } - } - } while (changedLiftedOwner) - - private def newName(sym: Symbol)(implicit ctx: Context): Name = - if (sym.isAnonymousFunction && sym.owner.is(Method, butNot = Label)) - (sym.name ++ NJ ++ sym.owner.name).freshened - else sym.name.freshened - - private def generateProxies()(implicit ctx: Context): Unit = - for ((owner, freeValues) <- free.toIterator) { - val newFlags = Synthetic | (if (owner.isClass) ParamAccessor | Private else Param) - ctx.debuglog(i"free var proxy: ${owner.showLocated}, ${freeValues.toList}%, %") - proxyMap(owner) = { - for (fv <- freeValues.toList) yield { - val proxyName = newName(fv) - val proxy = ctx.newSymbol(owner, proxyName.asTermName, newFlags, fv.info, coord = fv.coord) - if (owner.isClass) proxy.enteredAfter(thisTransform) - (fv, proxy) - } - }.toMap - } - - private def liftedInfo(local: Symbol)(implicit ctx: Context): Type = local.info match { - case mt @ MethodType(pnames, ptypes) => - val ps = proxies(local) - MethodType( - ps.map(_.name.asTermName) ++ pnames, - ps.map(_.info) ++ ptypes, - mt.resultType) - case info => info - } - - private def liftLocals()(implicit ctx: Context): Unit = { - for ((local, lOwner) <- liftedOwner) { - val (newOwner, maybeStatic) = - if (lOwner is Package) { - val encClass = local.enclosingClass - val topClass = local.topLevelClass - val preferEncClass = - encClass.isStatic && - // non-static classes can capture owners, so should be avoided - (encClass.isProperlyContainedIn(topClass) || - // can be false for symbols which are defined in some weird combination of supercalls. - encClass.is(ModuleClass, butNot = Package) - // needed to not cause deadlocks in classloader. see t5375.scala - ) - if (preferEncClass) (encClass, EmptyFlags) - else (topClass, JavaStatic) - } - else (lOwner, EmptyFlags) - local.copySymDenotation( - owner = newOwner, - name = newName(local), - initFlags = local.flags &~ (InSuperCall | Module) | Private | maybeStatic, - // drop Module because class is no longer a singleton in the lifted context. - info = liftedInfo(local)).installAfter(thisTransform) - } - for (local <- free.keys) - if (!liftedOwner.contains(local)) - local.copySymDenotation(info = liftedInfo(local)).installAfter(thisTransform) - } - - private def init(implicit ctx: Context) = { - (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree) - computeFreeVars() - computeLiftedOwners() - generateProxies()(ctx.withPhase(thisTransform.next)) - liftLocals()(ctx.withPhase(thisTransform.next)) - } - - override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { - val lifter = new LambdaLifter - lifter.init(ctx.withPhase(thisTransform)) - lifter - } - - private def currentEnclosure(implicit ctx: Context) = - ctx.owner.enclosingMethodOrClass - - private def inCurrentOwner(sym: Symbol)(implicit ctx: Context) = - sym.enclosure == currentEnclosure - - private def proxy(sym: Symbol)(implicit ctx: Context): Symbol = { - def liftedEnclosure(sym: Symbol) = liftedOwner.getOrElse(sym, sym.enclosure) - def searchIn(enclosure: Symbol): Symbol = { - if (!enclosure.exists) { - def enclosures(encl: Symbol): List[Symbol] = - if (encl.exists) encl :: enclosures(liftedEnclosure(encl)) else Nil - throw new IllegalArgumentException(i"Could not find proxy for ${sym.showDcl} in ${sym.ownersIterator.toList}, encl = $currentEnclosure, owners = ${currentEnclosure.ownersIterator.toList}%, %; enclosures = ${enclosures(currentEnclosure)}%, %") - } - ctx.debuglog(i"searching for $sym(${sym.owner}) in $enclosure") - proxyMap get enclosure match { - case Some(pmap) => - pmap get sym match { - case Some(proxy) => return proxy - case none => - } - case none => - } - searchIn(liftedEnclosure(enclosure)) - } - if (inCurrentOwner(sym)) sym else searchIn(currentEnclosure) - } - - private def memberRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = { - val clazz = sym.enclosingClass - val qual = - if (clazz.isStaticOwner || ctx.owner.enclosingClass == clazz) - singleton(clazz.thisType) - else if (ctx.owner.isConstructor) - outerParam.get(ctx.owner) match { - case Some(param) => outer.path(clazz, Ident(param.termRef)) - case _ => outer.path(clazz) - } - else outer.path(clazz) - transformFollowingDeep(qual.select(sym)) - } - - private def proxyRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = { - val psym = proxy(sym)(ctx.withPhase(thisTransform)) - transformFollowingDeep(if (psym.owner.isTerm) ref(psym) else memberRef(psym)) - } - - private def addFreeArgs(sym: Symbol, args: List[Tree])(implicit ctx: Context, info: TransformerInfo) = - free get sym match { - case Some(fvs) => fvs.toList.map(proxyRef(_)) ++ args - case _ => args - } - - private def addFreeParams(tree: Tree, proxies: List[Symbol])(implicit ctx: Context, info: TransformerInfo): Tree = proxies match { - case Nil => tree - case proxies => - val sym = tree.symbol - val freeParamDefs = proxies.map(proxy => - transformFollowingDeep(ValDef(proxy.asTerm).withPos(tree.pos)).asInstanceOf[ValDef]) - def proxyInit(field: Symbol, param: Symbol) = - transformFollowingDeep(memberRef(field).becomes(ref(param))) - - /** Initialize proxy fields from proxy parameters and map `rhs` from fields to parameters */ - def copyParams(rhs: Tree) = { - val fvs = freeVars(sym.owner) - val classProxies = fvs.map(proxyOf(sym.owner, _)) - val constrProxies = fvs.map(proxyOf(sym, _)) - ctx.debuglog(i"copy params ${constrProxies.map(_.showLocated)}%, % to ${classProxies.map(_.showLocated)}%, %}") - seq((classProxies, constrProxies).zipped.map(proxyInit), rhs) - } - - tree match { - case tree: DefDef => - cpy.DefDef(tree)( - vparamss = tree.vparamss.map(freeParamDefs ++ _), - rhs = - if (sym.isPrimaryConstructor && !sym.owner.is(Trait)) copyParams(tree.rhs) - else tree.rhs) - case tree: Template => - cpy.Template(tree)(body = freeParamDefs ++ tree.body) - } - } - - private def liftDef(tree: MemberDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - val buf = liftedDefs(tree.symbol.owner) - transformFollowing(rename(tree, tree.symbol.name)).foreachInThicket(buf += _) - EmptyTree - } - - private def needsLifting(sym: Symbol) = liftedOwner contains sym - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { - val sym = tree.symbol - tree.tpe match { - case tpe @ TermRef(prefix, _) => - if (prefix eq NoPrefix) - if (sym.enclosure != currentEnclosure && !sym.isStatic) - (if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos) - else if (sym.owner.isClass) // sym was lifted out - ref(sym).withPos(tree.pos) - else - tree - else if (!prefixIsElidable(tpe)) ref(tpe) - else tree - case _ => - tree - } - } - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = - cpy.Apply(tree)(tree.fun, addFreeArgs(tree.symbol, tree.args)).withPos(tree.pos) - - override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo) = - cpy.Closure(tree)(env = addFreeArgs(tree.meth.symbol, tree.env)) - - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = { - val sym = tree.symbol - val paramsAdded = - if (free.contains(sym)) addFreeParams(tree, proxies(sym)).asInstanceOf[DefDef] - else tree - if (needsLifting(sym)) liftDef(paramsAdded) - else paramsAdded - } - - override def transformReturn(tree: Return)(implicit ctx: Context, info: TransformerInfo) = tree.expr match { - case Block(stats, value) => - Block(stats, Return(value, tree.from)).withPos(tree.pos) - case _ => - tree - } - - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { - val cls = ctx.owner - val impl = addFreeParams(tree, proxies(cls)).asInstanceOf[Template] - cpy.Template(impl)(body = impl.body ++ liftedDefs.remove(cls).get) - } - - override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = - if (needsLifting(tree.symbol)) liftDef(tree) else tree - } -} diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala deleted file mode 100644 index e63a7c3a7..000000000 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ /dev/null @@ -1,418 +0,0 @@ -package dotty.tools.dotc -package transform - -import dotty.tools.dotc.core.Annotations.Annotation -import dotty.tools.dotc.core.Phases.NeedsCompanions - -import scala.collection.mutable -import core._ -import Contexts._ -import Symbols._ -import Decorators._ -import NameOps._ -import StdNames.nme -import rewrite.Rewrites.patch -import util.Positions.Position -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, MiniPhaseTransform} -import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.ast.{untpd, tpd} -import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.core.Types.{ExprType, NoType, MethodType} -import dotty.tools.dotc.core.Names.Name -import SymUtils._ -import scala.collection.mutable.ListBuffer -import dotty.tools.dotc.core.Denotations.SingleDenotation -import dotty.tools.dotc.core.SymDenotations.SymDenotation -import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer, DenotTransformer} -import Erasure.Boxing.adaptToType - -class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { - import LazyVals._ - - import tpd._ - - def transformer = new LazyVals - - val containerFlags = Flags.Synthetic | Flags.Mutable | Flags.Lazy - val initFlags = Flags.Synthetic | Flags.Method - - val containerFlagsMask = Flags.Method | Flags.Lazy | Flags.Accessor | Flags.Module - - /** this map contains mutable state of transformation: OffsetDefs to be appended to companion object definitions, - * and number of bits currently used */ - class OffsetInfo(var defs: List[Tree], var ord:Int) - val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo] - - override def phaseName: String = "LazyVals" - - /** List of names of phases that should have finished processing of tree - * before this phase starts processing same tree */ - override def runsAfter = Set(classOf[Mixin]) - - override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = - transformLazyVal(tree) - - - override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - transformLazyVal(tree) - } - - def transformLazyVal(tree: ValOrDefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - val sym = tree.symbol - if (!(sym is Flags.Lazy) || sym.owner.is(Flags.Trait) || (sym.isStatic && sym.is(Flags.Module))) tree - else { - val isField = sym.owner.isClass - if (isField) { - if (sym.isVolatile || - (sym.is(Flags.Module)/* || ctx.scala2Mode*/) && - // TODO assume @volatile once LazyVals uses static helper constructs instead of - // ones in the companion object. - !sym.is(Flags.Synthetic)) - // module class is user-defined. - // Should be threadsafe, to mimic safety guaranteed by global object - transformMemberDefVolatile(tree) - else if (sym.is(Flags.Module)) // synthetic module - transformSyntheticModule(tree) - else - transformMemberDefNonVolatile(tree) - } - else transformLocalDef(tree) - } - } - - - /** Append offset fields to companion objects - */ - override def transformTemplate(template: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - val cls = ctx.owner.asClass - - appendOffsetDefs.get(cls) match { - case None => template - case Some(data) => - data.defs.foreach(_.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot))) - cpy.Template(template)(body = addInFront(data.defs, template.body)) - } - - } - - private def addInFront(prefix: List[Tree], stats: List[Tree]) = stats match { - case first :: rest if isSuperConstrCall(first) => first :: prefix ::: rest - case _ => prefix ::: stats - } - - /** Make an eager val that would implement synthetic module. - * Eager val ensures thread safety and has less code generated. - * - */ - def transformSyntheticModule(tree: ValOrDefDef)(implicit ctx: Context) = { - val sym = tree.symbol - val holderSymbol = ctx.newSymbol(sym.owner, sym.asTerm.name.lazyLocalName, - Flags.Synthetic, sym.info.widen.resultType).enteredAfter(this) - val field = ValDef(holderSymbol, tree.rhs.changeOwnerAfter(sym, holderSymbol, this)) - val getter = DefDef(sym.asTerm, ref(holderSymbol)) - Thicket(field, getter) - } - - /** Replace a local lazy val inside a method, - * with a LazyHolder from - * dotty.runtime(eg dotty.runtime.LazyInt) - */ - def transformLocalDef(x: ValOrDefDef)(implicit ctx: Context) = { - val valueInitter = x.rhs - val holderName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName - val initName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL_INIT).toTermName - val tpe = x.tpe.widen.resultType.widen - - val holderType = - if (tpe isRef defn.IntClass) "LazyInt" - else if (tpe isRef defn.LongClass) "LazyLong" - else if (tpe isRef defn.BooleanClass) "LazyBoolean" - else if (tpe isRef defn.FloatClass) "LazyFloat" - else if (tpe isRef defn.DoubleClass) "LazyDouble" - else if (tpe isRef defn.ByteClass) "LazyByte" - else if (tpe isRef defn.CharClass) "LazyChar" - else if (tpe isRef defn.ShortClass) "LazyShort" - else "LazyRef" - - - val holderImpl = ctx.requiredClass("dotty.runtime." + holderType) - - val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.pos) - val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType(Nil, tpe), coord = x.pos) - val result = ref(holderSymbol).select(lazyNme.value) - val flag = ref(holderSymbol).select(lazyNme.initialized) - val initer = valueInitter.changeOwner(x.symbol, initSymbol) - val initBody = - adaptToType( - ref(holderSymbol).select(defn.Object_synchronized).appliedTo( - adaptToType(mkNonThreadSafeDef(result, flag, initer), defn.ObjectType)), - tpe) - val initTree = DefDef(initSymbol, initBody) - val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List())) - val methodBody = tpd.If(flag.ensureApplied, - result.ensureApplied, - ref(initSymbol).ensureApplied).ensureConforms(tpe) - - val methodTree = DefDef(x.symbol.asTerm, methodBody) - ctx.debuglog(s"found a lazy val ${x.show},\n rewrote with ${holderTree.show}") - Thicket(holderTree, initTree, methodTree) - } - - - override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = { - // backend requires field usage to be after field definition - // need to bring containers to start of method - val (holders, stats) = - atGroupEnd { implicit ctx: Context => - trees.partition { - _.symbol.flags.&~(Flags.Touched) == containerFlags - // Filtering out Flags.Touched is not required currently, as there are no LazyTypes involved here - // but just to be more safe - } - } - holders:::stats - } - - /** Create non-threadsafe lazy accessor equivalent to such code - * def methodSymbol() = { - * if (flag) target - * else { - * target = rhs - * flag = true - * target - * } - * } - */ - - def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = { - val setFlag = flag.becomes(Literal(Constants.Constant(true))) - val setTargets = if (isWildcardArg(rhs)) Nil else target.becomes(rhs) :: Nil - val init = Block(setFlag :: setTargets, target.ensureApplied) - If(flag.ensureApplied, target.ensureApplied, init) - } - - /** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code - * def methodSymbol() = { - * if (target eq null) { - * target = rhs - * target - * } else target - * } - */ - def mkDefNonThreadSafeNonNullable(target: Symbol, rhs: Tree)(implicit ctx: Context) = { - val cond = ref(target).select(nme.eq).appliedTo(Literal(Constant(null))) - val exp = ref(target) - val setTarget = exp.becomes(rhs) - val init = Block(List(setTarget), exp) - If(cond, init, exp) - } - - def transformMemberDefNonVolatile(x: ValOrDefDef)(implicit ctx: Context) = { - val claz = x.symbol.owner.asClass - val tpe = x.tpe.widen.resultType.widen - assert(!(x.symbol is Flags.Mutable)) - val containerName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName - val containerSymbol = ctx.newSymbol(claz, containerName, - x.symbol.flags &~ containerFlagsMask | containerFlags | Flags.Private, - tpe, coord = x.symbol.coord - ).enteredAfter(this) - - val containerTree = ValDef(containerSymbol, defaultValue(tpe)) - if (x.tpe.isNotNull && tpe <:< defn.ObjectType) { // can use 'null' value instead of flag - val slowPath = DefDef(x.symbol.asTerm, mkDefNonThreadSafeNonNullable(containerSymbol, x.rhs)) - Thicket(containerTree, slowPath) - } - else { - val flagName = ctx.freshName(x.name ++ StdNames.nme.BITMAP_PREFIX).toTermName - val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags | Flags.Private, defn.BooleanType).enteredAfter(this) - val flag = ValDef(flagSymbol, Literal(Constants.Constant(false))) - val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), x.rhs)) - Thicket(containerTree, flag, slowPath) - } - } - - /** Create a threadsafe lazy accessor equivalent to such code - * - * def methodSymbol(): Int = { - * val result: Int = 0 - * val retry: Boolean = true - * var flag: Long = 0L - * while retry do { - * flag = dotty.runtime.LazyVals.get(this, $claz.$OFFSET) - * dotty.runtime.LazyVals.STATE(flag, 0) match { - * case 0 => - * if dotty.runtime.LazyVals.CAS(this, $claz.$OFFSET, flag, 1, $ord) { - * try {result = rhs} catch { - * case x: Throwable => - * dotty.runtime.LazyVals.setFlag(this, $claz.$OFFSET, 0, $ord) - * throw x - * } - * $target = result - * dotty.runtime.LazyVals.setFlag(this, $claz.$OFFSET, 3, $ord) - * retry = false - * } - * case 1 => - * dotty.runtime.LazyVals.wait4Notification(this, $claz.$OFFSET, flag, $ord) - * case 2 => - * dotty.runtime.LazyVals.wait4Notification(this, $claz.$OFFSET, flag, $ord) - * case 3 => - * retry = false - * result = $target - * } - * } - * result - * } - */ - def mkThreadSafeDef(methodSymbol: TermSymbol, claz: ClassSymbol, ord: Int, target: Symbol, rhs: Tree, tp: Types.Type, offset: Tree, getFlag: Tree, stateMask: Tree, casFlag: Tree, setFlagState: Tree, waitOnLock: Tree)(implicit ctx: Context) = { - val initState = Literal(Constants.Constant(0)) - val computeState = Literal(Constants.Constant(1)) - val notifyState = Literal(Constants.Constant(2)) - val computedState = Literal(Constants.Constant(3)) - val flagSymbol = ctx.newSymbol(methodSymbol, lazyNme.flag, containerFlags, defn.LongType) - val flagDef = ValDef(flagSymbol, Literal(Constant(0L))) - - val thiz = This(claz)(ctx.fresh.setOwner(claz)) - - val resultSymbol = ctx.newSymbol(methodSymbol, lazyNme.result, containerFlags, tp) - val resultDef = ValDef(resultSymbol, defaultValue(tp)) - - val retrySymbol = ctx.newSymbol(methodSymbol, lazyNme.retry, containerFlags, defn.BooleanType) - val retryDef = ValDef(retrySymbol, Literal(Constants.Constant(true))) - - val whileCond = ref(retrySymbol) - - val compute = { - val handlerSymbol = ctx.newSymbol(methodSymbol, nme.ANON_FUN, Flags.Synthetic, - MethodType(List(nme.x_1), List(defn.ThrowableType), defn.IntType)) - val caseSymbol = ctx.newSymbol(methodSymbol, nme.DEFAULT_EXCEPTION_NAME, Flags.Synthetic, defn.ThrowableType) - val triggerRetry = setFlagState.appliedTo(thiz, offset, initState, Literal(Constant(ord))) - val complete = setFlagState.appliedTo(thiz, offset, computedState, Literal(Constant(ord))) - - val handler = CaseDef(Bind(caseSymbol, ref(caseSymbol)), EmptyTree, - Block(List(triggerRetry), Throw(ref(caseSymbol)) - )) - - val compute = ref(resultSymbol).becomes(rhs) - val tr = Try(compute, List(handler), EmptyTree) - val assign = ref(target).becomes(ref(resultSymbol)) - val noRetry = ref(retrySymbol).becomes(Literal(Constants.Constant(false))) - val body = If(casFlag.appliedTo(thiz, offset, ref(flagSymbol), computeState, Literal(Constant(ord))), - Block(tr :: assign :: complete :: noRetry :: Nil, Literal(Constant(()))), - Literal(Constant(()))) - - CaseDef(initState, EmptyTree, body) - } - - val waitFirst = { - val wait = waitOnLock.appliedTo(thiz, offset, ref(flagSymbol), Literal(Constant(ord))) - CaseDef(computeState, EmptyTree, wait) - } - - val waitSecond = { - val wait = waitOnLock.appliedTo(thiz, offset, ref(flagSymbol), Literal(Constant(ord))) - CaseDef(notifyState, EmptyTree, wait) - } - - val computed = { - val noRetry = ref(retrySymbol).becomes(Literal(Constants.Constant(false))) - val result = ref(resultSymbol).becomes(ref(target)) - val body = Block(noRetry :: result :: Nil, Literal(Constant(()))) - CaseDef(computedState, EmptyTree, body) - } - - val default = CaseDef(Underscore(defn.LongType), EmptyTree, Literal(Constant(()))) - - val cases = Match(stateMask.appliedTo(ref(flagSymbol), Literal(Constant(ord))), - List(compute, waitFirst, waitSecond, computed, default)) //todo: annotate with @switch - - val whileBody = List(ref(flagSymbol).becomes(getFlag.appliedTo(thiz, offset)), cases) - val cycle = WhileDo(methodSymbol, whileCond, whileBody) - DefDef(methodSymbol, Block(resultDef :: retryDef :: flagDef :: cycle :: Nil, ref(resultSymbol))) - } - - def transformMemberDefVolatile(x: ValOrDefDef)(implicit ctx: Context) = { - assert(!(x.symbol is Flags.Mutable)) - - val tpe = x.tpe.widen.resultType.widen - val claz = x.symbol.owner.asClass - val thizClass = Literal(Constant(claz.info)) - val helperModule = ctx.requiredModule("dotty.runtime.LazyVals") - val getOffset = Select(ref(helperModule), lazyNme.RLazyVals.getOffset) - var offsetSymbol: TermSymbol = null - var flag: Tree = EmptyTree - var ord = 0 - - def offsetName(id: Int) = (StdNames.nme.LAZY_FIELD_OFFSET + (if(x.symbol.owner.is(Flags.Module)) "_m_" else "") + id.toString).toTermName - - // compute or create appropriate offsetSymol, bitmap and bits used by current ValDef - appendOffsetDefs.get(claz) match { - case Some(info) => - val flagsPerLong = (64 / dotty.runtime.LazyVals.BITS_PER_LAZY_VAL).toInt - info.ord += 1 - ord = info.ord % flagsPerLong - val id = info.ord / flagsPerLong - val offsetById = offsetName(id) - if (ord != 0) { // there are unused bits in already existing flag - offsetSymbol = claz.info.decl(offsetById) - .suchThat(sym => (sym is Flags.Synthetic) && sym.isTerm) - .symbol.asTerm - } else { // need to create a new flag - offsetSymbol = ctx.newSymbol(claz, offsetById, Flags.Synthetic, defn.LongType).enteredAfter(this) - offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)) - val flagName = (StdNames.nme.BITMAP_PREFIX + id.toString).toTermName - val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) - flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) - val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thizClass, Literal(Constant(flagName.toString)))) - info.defs = offsetTree :: info.defs - } - - case None => - offsetSymbol = ctx.newSymbol(claz, offsetName(0), Flags.Synthetic, defn.LongType).enteredAfter(this) - offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)) - val flagName = (StdNames.nme.BITMAP_PREFIX + "0").toTermName - val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) - flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) - val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thizClass, Literal(Constant(flagName.toString)))) - appendOffsetDefs += (claz -> new OffsetInfo(List(offsetTree), ord)) - } - - val containerName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName - val containerSymbol = ctx.newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags, tpe, coord = x.symbol.coord).enteredAfter(this) - - val containerTree = ValDef(containerSymbol, defaultValue(tpe)) - - val offset = ref(offsetSymbol) - val getFlag = Select(ref(helperModule), lazyNme.RLazyVals.get) - val setFlag = Select(ref(helperModule), lazyNme.RLazyVals.setFlag) - val wait = Select(ref(helperModule), lazyNme.RLazyVals.wait4Notification) - val state = Select(ref(helperModule), lazyNme.RLazyVals.state) - val cas = Select(ref(helperModule), lazyNme.RLazyVals.cas) - - val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, x.rhs, tpe, offset, getFlag, state, cas, setFlag, wait) - if (flag eq EmptyTree) - Thicket(containerTree, accessor) - else Thicket(containerTree, flag, accessor) - } -} - -object LazyVals { - object lazyNme { - object RLazyVals { - import dotty.runtime.LazyVals._ - val get = Names.get.toTermName - val setFlag = Names.setFlag.toTermName - val wait4Notification = Names.wait4Notification.toTermName - val state = Names.state.toTermName - val cas = Names.cas.toTermName - val getOffset = Names.getOffset.toTermName - } - val flag = "flag".toTermName - val result = "result".toTermName - val value = "value".toTermName - val initialized = "initialized".toTermName - val retry = "retry".toTermName - } -} - - - diff --git a/src/dotty/tools/dotc/transform/LiftTry.scala b/src/dotty/tools/dotc/transform/LiftTry.scala deleted file mode 100644 index 6a273b91e..000000000 --- a/src/dotty/tools/dotc/transform/LiftTry.scala +++ /dev/null @@ -1,66 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import NonLocalReturns._ - -/** Lifts try's that might be executed on non-empty expression stacks - * to their own methods. I.e. - * - * try body catch handler - * - * is lifted to - * - * { def liftedTree$n() = try body catch handler; liftedTree$n() } - */ -class LiftTry extends MiniPhase with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - /** the following two members override abstract members in Transform */ - val phaseName: String = "liftTry" - - val treeTransform = new Transform(needLift = false) - val liftingTransform = new Transform(needLift = true) - - class Transform(needLift: Boolean) extends TreeTransform { - def phase = thisTransform - - override def prepareForApply(tree: Apply)(implicit ctx: Context) = - if (tree.fun.symbol.is(Label)) this - else liftingTransform - - override def prepareForValDef(tree: ValDef)(implicit ctx: Context) = - if (!tree.symbol.exists || - tree.symbol.isSelfSym || - tree.symbol.owner == ctx.owner.enclosingMethod) this - else liftingTransform - - override def prepareForAssign(tree: Assign)(implicit ctx: Context) = - if (tree.lhs.symbol.maybeOwner == ctx.owner.enclosingMethod) this - else liftingTransform - - override def prepareForReturn(tree: Return)(implicit ctx: Context) = - if (!isNonLocalReturn(tree)) this - else liftingTransform - - override def prepareForTemplate(tree: Template)(implicit ctx: Context) = - treeTransform - - override def transformTry(tree: Try)(implicit ctx: Context, info: TransformerInfo): Tree = - if (needLift) { - ctx.debuglog(i"lifting tree at ${tree.pos}, current owner = ${ctx.owner}") - val fn = ctx.newSymbol( - ctx.owner, ctx.freshName("liftedTree").toTermName, Synthetic | Method, - MethodType(Nil, tree.tpe), coord = tree.pos) - tree.changeOwnerAfter(ctx.owner, fn, thisTransform) - Block(DefDef(fn, tree) :: Nil, ref(fn).appliedToNone) - } - else tree - } -} diff --git a/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala b/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala deleted file mode 100644 index ca06938dc..000000000 --- a/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala +++ /dev/null @@ -1,62 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TreeTransforms._ -import Contexts.Context -import Flags._ -import SymUtils._ -import Symbols._ -import SymDenotations._ -import Types._ -import Decorators._ -import DenotTransformers._ -import StdNames._ -import NameOps._ -import Phases._ -import ast.untpd -import ast.Trees._ -import collection.mutable - -/** Rewrite calls - * - * super[M].f(args) - * - * where M is a Scala2 trait implemented by the current class to - * - * M$class.f(this, args) - * - * provided the implementation class M$class defines a corresponding function `f`. - */ -class LinkScala2ImplClasses extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - override def phaseName: String = "linkScala2ImplClasses" - - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) - - override def transformApply(app: Apply)(implicit ctx: Context, info: TransformerInfo) = { - def currentClass = ctx.owner.enclosingClass.asClass - app match { - case Apply(sel @ Select(Super(_, _), _), args) - if sel.symbol.owner.is(Scala2xTrait) && currentClass.mixins.contains(sel.symbol.owner) => - val impl = implMethod(sel.symbol) - if (impl.exists) Apply(ref(impl), This(currentClass) :: args).withPos(app.pos) - else app // could have been an abstract method in a trait linked to from a super constructor - case _ => - app - } - } - - private def implMethod(meth: Symbol)(implicit ctx: Context): Symbol = { - val implInfo = meth.owner.implClass.info - if (meth.isConstructor) - implInfo.decl(nme.TRAIT_CONSTRUCTOR).symbol - else - implInfo.decl(meth.name) - .suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature) - .symbol - } - - private val Scala2xTrait = allOf(Scala2x, Trait) -} diff --git a/src/dotty/tools/dotc/transform/Literalize.scala.disabled b/src/dotty/tools/dotc/transform/Literalize.scala.disabled deleted file mode 100644 index f33baa52b..000000000 --- a/src/dotty/tools/dotc/transform/Literalize.scala.disabled +++ /dev/null @@ -1,95 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import core.StdNames.nme -import ast.Trees._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Constants._ - -/** This phase rewrites idempotent expressions with constant types to Literals. - * The constant types are eliminated by erasure, so we need to keep - * the info about constantness in the trees. - * - * The phase also makes sure that the constant of a literal is the same as the constant - * in the type of the literal. - */ -class Literalize extends MiniPhaseTransform { thisTransform => - import ast.tpd._ - - override def phaseName: String = "literalize" - - /** Note: Demanding idempotency instead of purity is strictly speaking too loose. - * Example - * - * object O { final val x = 42; println("43") } - * O.x - * - * Strictly speaking we can't replace `O.x` with `42`. But this would make - * most expressions non-constant. Maybe we can change the spec to accept this - * kind of eliding behavior. Or else enforce true purity in the compiler. - * The choice will be affected by what we will do with `inline` and with - * Singleton type bounds (see SIP 23). Presumably - * - * object O1 { val x: Singleton = 42; println("43") } - * object O2 { inline val x = 42; println("43") } - * - * should behave differently. - * - * O1.x should have the same effect as { println("43"; 42 } - * - * whereas - * - * O2.x = 42 - * - * Revisit this issue once we have implemented `inline`. Then we can demand - * purity of the prefix unless the selection goes to an inline val. - */ - def literalize(tree: Tree)(implicit ctx: Context): Tree = { - def recur(tp: Type): Tree = tp match { - case ConstantType(value) if isIdempotentExpr(tree) => Literal(value) - case tp: TermRef if tp.symbol.isStable => recur(tp.info.widenExpr) - case _ => tree - } - recur(tree.tpe) - } - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = - literalize(tree) - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = - literalize(tree) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - literalize(tree) - - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = - literalize(tree) - - override def transformLiteral(tree: Literal)(implicit ctx: Context, info: TransformerInfo): Tree = tree.tpe match { - case ConstantType(const) if tree.const.value != const.value || (tree.const.tag != const.tag) => Literal(const) - case _ => tree - } - - /** Check that all literals have types match underlying constants - */ - override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { - tree match { - case Literal(c @ Constant(treeValue)) => - tree.tpe match { - case ConstantType(c2 @ Constant(typeValue)) => - assert(treeValue == typeValue && c2.tag == c.tag, - i"Type of Literal $tree is inconsistent with underlying constant") - case tpe => - assert(c.tpe =:= tpe, i"Type of Literal $tree is inconsistent with underlying constant type ${c.tpe}") - } - case _ => - } - } -} diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala deleted file mode 100644 index 9634decaa..000000000 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ /dev/null @@ -1,70 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import typer._ -import Phases._ -import ast.Trees._ -import Contexts._ -import Symbols._ -import Flags.PackageVal -import Decorators._ - -/** A base class for transforms. - * A transform contains a compiler phase which applies a tree transformer. - */ -abstract class MacroTransform extends Phase { - - import ast.tpd._ - - override def run(implicit ctx: Context): Unit = { - val unit = ctx.compilationUnit - unit.tpdTree = newTransformer.transform(unit.tpdTree)(ctx.withPhase(transformPhase)) - } - - protected def newTransformer(implicit ctx: Context): Transformer - - /** The phase in which the transformation should be run. - * By default this is the phase given by the this macro transformer, - * but it could be overridden to be the phase following that one. - */ - protected def transformPhase(implicit ctx: Context): Phase = this - - class Transformer extends TreeMap { - - protected def localCtx(tree: Tree)(implicit ctx: Context) = { - val sym = tree.symbol - val owner = if (sym is PackageVal) sym.moduleClass else sym - ctx.fresh.setTree(tree).setOwner(owner) - } - - def transformStats(trees: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { - def transformStat(stat: Tree): Tree = stat match { - case _: Import | _: DefTree => transform(stat) - case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) - case _ => transform(stat)(ctx.exprContext(stat, exprOwner)) - } - flatten(trees.mapconserve(transformStat(_))) - } - - override def transform(tree: Tree)(implicit ctx: Context): Tree = { - tree match { - case EmptyValDef => - tree - case _: PackageDef | _: MemberDef => - super.transform(tree)(localCtx(tree)) - case impl @ Template(constr, parents, self, _) => - cpy.Template(tree)( - transformSub(constr), - transform(parents)(ctx.superCallContext), - transformSelf(self), - transformStats(impl.body, tree.symbol)) - case _ => - super.transform(tree) - } - } - - def transformSelf(vd: ValDef)(implicit ctx: Context) = - cpy.ValDef(vd)(tpt = transform(vd.tpt)) - } -} diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala deleted file mode 100644 index 01c240e3a..000000000 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ /dev/null @@ -1,129 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers._ -import Phases.Phase -import Contexts.Context -import SymDenotations.SymDenotation -import Types._ -import Symbols._ -import SymUtils._ -import Constants._ -import ast.Trees._ -import TreeTransforms._ -import NameOps._ -import Flags._ -import Decorators._ - -/** Provides the implementations of all getters and setters, introducing - * fields to hold the value accessed by them. - * TODO: Make LazyVals a part of this phase? - * - * <accessor> <stable> <mods> def x(): T = e - * --> private val x: T = e - * <accessor> <stable> <mods> def x(): T = x - * - * <accessor> <mods> def x(): T = e - * --> private var x: T = e - * <accessor> <mods> def x(): T = x - * - * <accessor> <mods> def x_=(y: T): Unit = () - * --> <accessor> <mods> def x_=(y: T): Unit = x = y - */ - class Memoize extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - override def phaseName = "memoize" - - /* Makes sure that, after getters and constructors gen, there doesn't - * exist non-deferred definitions that are not implemented. */ - override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { - def errorLackImplementation(t: Tree) = { - val firstPhaseId = t.symbol.initial.validFor.firstPhaseId - val definingPhase = ctx.withPhase(firstPhaseId).phase.prev - throw new AssertionError( - i"Non-deferred definition introduced by $definingPhase lacks implementation: $t") - } - tree match { - case ddef: DefDef - if !ddef.symbol.is(Deferred) && ddef.rhs == EmptyTree => - errorLackImplementation(ddef) - case tdef: TypeDef - if tdef.symbol.isClass && !tdef.symbol.is(Deferred) && tdef.rhs == EmptyTree => - errorLackImplementation(tdef) - case _ => - } - super.checkPostCondition(tree) - } - - /** Should run after mixin so that fields get generated in the - * class that contains the concrete getter rather than the trait - * that defines it. - */ - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) - - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - val sym = tree.symbol - - def newField = { - val fieldType = - if (sym.isGetter) sym.info.resultType - else /*sym.isSetter*/ sym.info.firstParamTypes.head - - ctx.newSymbol( - owner = ctx.owner, - name = sym.name.asTermName.fieldName, - flags = Private | (if (sym is Stable) EmptyFlags else Mutable), - info = fieldType, - coord = tree.pos) - .withAnnotationsCarrying(sym, defn.FieldMetaAnnot) - .enteredAfter(thisTransform) - } - - /** Can be used to filter annotations on getters and setters; not used yet */ - def keepAnnotations(denot: SymDenotation, meta: ClassSymbol) = { - val cpy = sym.copySymDenotation() - cpy.filterAnnotations(_.symbol.derivesFrom(meta)) - if (cpy.annotations ne denot.annotations) cpy.installAfter(thisTransform) - } - - lazy val field = sym.field.orElse(newField).asTerm - - def adaptToField(tree: Tree) = - if (tree.isEmpty) tree else tree.ensureConforms(field.info.widen) - - if (sym.is(Accessor, butNot = NoFieldNeeded)) - if (sym.isGetter) { - def skipBlocks(t: Tree): Tree = t match { - case Block(_, t1) => skipBlocks(t1) - case _ => t - } - skipBlocks(tree.rhs) match { - case lit: Literal if sym.is(Final) && isIdempotentExpr(tree.rhs) => - // duplicating scalac behavior: for final vals that have rhs as constant, we do not create a field - // and instead return the value. This seemingly minor optimization has huge effect on initialization - // order and the values that can be observed during superconstructor call - - // see remark about idempotency in PostTyper#normalizeTree - cpy.DefDef(tree)(rhs = lit) - case _ => - var rhs = tree.rhs.changeOwnerAfter(sym, field, thisTransform) - if (isWildcardArg(rhs)) rhs = EmptyTree - - val fieldDef = transformFollowing(ValDef(field, adaptToField(rhs))) - val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))(ctx.withOwner(sym), info)) - Thicket(fieldDef, getterDef) - } - } else if (sym.isSetter) { - if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } // this is intended as an assertion - field.setFlag(Mutable) // necessary for vals mixed in from Scala2 traits - val initializer = Assign(ref(field), adaptToField(ref(tree.vparamss.head.head.symbol))) - cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(ctx.withOwner(sym), info)) - } - else tree // curiously, some accessors from Scala2 have ' ' suffixes. They count as - // neither getters nor setters - else tree - } - private val NoFieldNeeded = Lazy | Deferred | JavaDefined -} diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala deleted file mode 100644 index 27cfc835a..000000000 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ /dev/null @@ -1,257 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TreeTransforms._ -import Contexts.Context -import Flags._ -import SymUtils._ -import Symbols._ -import SymDenotations._ -import Types._ -import Decorators._ -import DenotTransformers._ -import StdNames._ -import NameOps._ -import Phases._ -import ast.untpd -import ast.Trees._ -import collection.mutable - -/** This phase performs the following transformations: - * - * 1. (done in `traitDefs` and `transformSym`) Map every concrete trait getter - * - * <mods> def x(): T = expr - * - * to the pair of definitions: - * - * <mods> def x(): T - * protected def initial$x(): T = { stats; expr } - * - * where `stats` comprises all statements between either the start of the trait - * or the previous field definition which are not definitions (i.e. are executed for - * their side effects). - * - * 2. (done in `traitDefs`) Make every concrete trait setter - * - * <mods> def x_=(y: T) = () - * - * deferred by mapping it to - * - * <mods> def x_=(y: T) - * - * 3. For a non-trait class C: - * - * For every trait M directly implemented by the class (see SymUtils.mixin), in - * reverse linearization order, add the following definitions to C: - * - * 3.1 (done in `traitInits`) For every parameter accessor `<mods> def x(): T` in M, - * in order of textual occurrence, add - * - * <mods> def x() = e - * - * where `e` is the constructor argument in C that corresponds to `x`. Issue - * an error if no such argument exists. - * - * 3.2 (done in `traitInits`) For every concrete trait getter `<mods> def x(): T` in M - * which is not a parameter accessor, in order of textual occurrence, produce the following: - * - * 3.2.1 If `x` is also a member of `C`, and M is a Dotty trait: - * - * <mods> def x(): T = super[M].initial$x() - * - * 3.2.2 If `x` is also a member of `C`, and M is a Scala 2.x trait: - * - * <mods> def x(): T = _ - * - * 3.2.3 If `x` is not a member of `C`, and M is a Dotty trait: - * - * super[M].initial$x() - * - * 3.2.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added. - * - * - * 3.3 (done in `superCallOpt`) The call: - * - * super[M].<init> - * - * 3.4 (done in `setters`) For every concrete setter `<mods> def x_=(y: T)` in M: - * - * <mods> def x_=(y: T) = () - * - * 4. (done in `transformTemplate` and `transformSym`) Drop all parameters from trait - * constructors. - * - * 5. (done in `transformSym`) Drop ParamAccessor flag from all parameter accessors in traits. - * - * Conceptually, this is the second half of the previous mixin phase. It needs to run - * after erasure because it copies references to possibly private inner classes and objects - * into enclosing classes where they are not visible. This can only be done if all references - * are symbolic. - */ -class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => - import ast.tpd._ - - override def phaseName: String = "mixin" - - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) - - override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = - if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait)) { - val sym1 = - if (sym is Lazy) sym - else sym.copySymDenotation(initFlags = sym.flags &~ ParamAccessor | Deferred) - sym1.ensureNotPrivate - } - else if (sym.isConstructor && sym.owner.is(Trait)) - sym.copySymDenotation( - name = nme.TRAIT_CONSTRUCTOR, - info = MethodType(Nil, sym.info.resultType)) - else - sym - - private def initializer(sym: Symbol)(implicit ctx: Context): TermSymbol = { - if (sym is Lazy) sym - else { - val initName = InitializerName(sym.name.asTermName) - sym.owner.info.decl(initName).symbol - .orElse( - ctx.newSymbol( - sym.owner, - initName, - Protected | Synthetic | Method, - sym.info, - coord = sym.symbol.coord).enteredAfter(thisTransform)) - } - }.asTerm - - override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { - val cls = impl.symbol.owner.asClass - val ops = new MixinOps(cls, thisTransform) - import ops._ - - def traitDefs(stats: List[Tree]): List[Tree] = { - val initBuf = new mutable.ListBuffer[Tree] - stats.flatMap({ - case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) => - // make initializer that has all effects of previous getter, - // replace getter rhs with empty tree. - val vsym = stat.symbol - val isym = initializer(vsym) - val rhs = Block( - initBuf.toList.map(_.changeOwnerAfter(impl.symbol, isym, thisTransform)), - stat.rhs.changeOwnerAfter(vsym, isym, thisTransform).wildcardToDefault) - initBuf.clear() - cpy.DefDef(stat)(rhs = EmptyTree) :: DefDef(isym, rhs) :: Nil - case stat: DefDef if stat.symbol.isSetter => - cpy.DefDef(stat)(rhs = EmptyTree) :: Nil - case stat: DefTree => - stat :: Nil - case stat => - initBuf += stat - Nil - }) ++ initBuf - } - - /** Map constructor call to a pair of a supercall and a list of arguments - * to be used as initializers of trait parameters if the target of the call - * is a trait. - */ - def transformConstructor(tree: Tree): (Tree, List[Tree]) = { - val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree - val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil) - (superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs) - } - - val superCallsAndArgs = ( - for (p <- impl.parents if p.symbol.isConstructor) - yield p.symbol.owner -> transformConstructor(p) - ).toMap - val superCalls = superCallsAndArgs.mapValues(_._1) - val initArgs = superCallsAndArgs.mapValues(_._2) - - def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match { - case Some(call) => - if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil - case None => - if (baseCls.is(NoInitsTrait) || defn.PhantomClasses.contains(baseCls)) Nil - else { - //println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}") - transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil - } - } - - def was(sym: Symbol, flags: FlagSet) = - ctx.atPhase(thisTransform) { implicit ctx => sym is flags } - - def traitInits(mixin: ClassSymbol): List[Tree] = { - var argNum = 0 - def nextArgument() = initArgs.get(mixin) match { - case Some(arguments) => - val result = arguments(argNum) - argNum += 1 - result - case None => - assert( - impl.parents.forall(_.tpe.typeSymbol != mixin), - i"missing parameters for $mixin from $impl should have been caught in typer") - ctx.error( - em"""parameterized $mixin is indirectly implemented, - |needs to be implemented directly so that arguments can be passed""", - cls.pos) - EmptyTree - } - - for (getter <- mixin.info.decls.toList if getter.isGetter && !was(getter, Deferred)) yield { - val isScala2x = mixin.is(Scala2x) - def default = Underscore(getter.info.resultType) - def initial = transformFollowing(superRef(initializer(getter)).appliedToNone) - - /** A call to the implementation of `getter` in `mixin`'s implementation class */ - def lazyGetterCall = { - def canbeImplClassGetter(sym: Symbol) = sym.info.firstParamTypes match { - case t :: Nil => t.isDirectRef(mixin) - case _ => false - } - val implClassGetter = mixin.implClass.info.nonPrivateDecl(getter.name) - .suchThat(canbeImplClassGetter).symbol - ref(mixin.implClass).select(implClassGetter).appliedTo(This(cls)) - } - - if (isCurrent(getter) || getter.is(ExpandedName)) { - val rhs = - if (was(getter, ParamAccessor)) nextArgument() - else if (isScala2x) - if (getter.is(Lazy, butNot = Module)) lazyGetterCall - else if (getter.is(Module)) - New(getter.info.resultType, List(This(cls))) - else Underscore(getter.info.resultType) - else initial - // transformFollowing call is needed to make memoize & lazy vals run - transformFollowing(DefDef(implementation(getter.asTerm), rhs)) - } - else if (isScala2x || was(getter, ParamAccessor)) EmptyTree - else initial - } - } - - def setters(mixin: ClassSymbol): List[Tree] = - for (setter <- mixin.info.decls.filter(setr => setr.isSetter && !was(setr, Deferred)).toList) - yield transformFollowing(DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos))) - - cpy.Template(impl)( - constr = - if (cls.is(Trait)) cpy.DefDef(impl.constr)(vparamss = Nil :: Nil) - else impl.constr, - parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)), - body = - if (cls is Trait) traitDefs(impl.body) - else { - val mixInits = mixins.flatMap { mixin => - flatten(traitInits(mixin)) ::: superCallOpt(mixin) ::: setters(mixin) - } - superCallOpt(superCls) ::: mixInits ::: impl.body - }) - } -} diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala deleted file mode 100644 index 6cebf7197..000000000 --- a/src/dotty/tools/dotc/transform/MixinOps.scala +++ /dev/null @@ -1,68 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Symbols._, Types._, Contexts._, SymDenotations._, DenotTransformers._, Flags._ -import util.Positions._ -import SymUtils._ -import StdNames._, NameOps._ - -class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: Context) { - import ast.tpd._ - - val superCls: Symbol = cls.superClass - val mixins: List[ClassSymbol] = cls.mixins - - def implementation(member: TermSymbol): TermSymbol = { - val res = member.copy( - owner = cls, - name = member.name.stripScala2LocalSuffix, - flags = member.flags &~ Deferred, - info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm - res.addAnnotations(member.annotations) - res - } - - def superRef(target: Symbol, pos: Position = cls.pos): Tree = { - val sup = if (target.isConstructor && !target.owner.is(Trait)) - Super(This(cls), tpnme.EMPTY, true) - else - Super(This(cls), target.owner.name.asTypeName, false, target.owner) - //println(i"super ref $target on $sup") - ast.untpd.Select(sup.withPos(pos), target.name) - .withType(NamedType.withFixedSym(sup.tpe, target)) - //sup.select(target) - } - - /** Is `sym` a member of implementing class `cls`? - * The test is performed at phase `thisTransform`. - */ - def isCurrent(sym: Symbol) = - ctx.atPhase(thisTransform) { implicit ctx => - cls.info.member(sym.name).hasAltWith(_.symbol == sym) - } - - /** Does `method` need a forwarder to in class `cls` - * Method needs a forwarder in those cases: - * - there's a class defining a method with same signature - * - there are multiple traits defining method with same signature - */ - def needsForwarder(meth: Symbol): Boolean = { - lazy val competingMethods = cls.baseClasses.iterator - .filter(_ ne meth.owner) - .map(meth.overriddenSymbol) - .filter(_.exists) - .toList - - def needsDisambiguation = competingMethods.exists(x=> !(x is Deferred)) // multiple implementations are available - def hasNonInterfaceDefinition = competingMethods.exists(!_.owner.is(Trait)) // there is a definition originating from class - meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && - isCurrent(meth) && - (needsDisambiguation || hasNonInterfaceDefinition || meth.owner.is(Scala2x)) - } - - final val PrivateOrAccessorOrDeferred = Private | Accessor | Deferred - - def forwarder(target: Symbol) = (targs: List[Type]) => (vrefss: List[List[Tree]]) => - superRef(target).appliedToTypes(targs).appliedToArgss(vrefss) -} diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala deleted file mode 100644 index 5c2cd3145..000000000 --- a/src/dotty/tools/dotc/transform/MoveStatics.scala +++ /dev/null @@ -1,77 +0,0 @@ -package dotty.tools.dotc.transform - -import dotty.tools.dotc.ast.{Trees, tpd} -import dotty.tools.dotc.core.Annotations.Annotation -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.DenotTransformers.{InfoTransformer, SymTransformer} -import dotty.tools.dotc.core.SymDenotations.SymDenotation -import dotty.tools.dotc.core.Decorators._ -import dotty.tools.dotc.core.NameOps._ -import dotty.tools.dotc.core.{Flags, Names} -import dotty.tools.dotc.core.Names.Name -import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.core.Types.MethodType -import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} - -/** Move static methods from companion to the class itself */ -class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransformer => - - import tpd._ - override def phaseName = "moveStatic" - - - def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { - if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module) && sym.owner.companionClass.exists) { - sym.owner.asClass.delete(sym.symbol) - sym.owner.companionClass.asClass.enter(sym.symbol) - val flags = if (sym.is(Flags.Method)) sym.flags else sym.flags | Flags.Mutable - sym.copySymDenotation(owner = sym.owner.companionClass, initFlags = flags) - } - else sym - } - - override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = { - if (ctx.owner.is(Flags.Package)) { - val (classes, others) = trees.partition(x => x.isInstanceOf[TypeDef] && x.symbol.isClass) - val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf[Map[Name, List[TypeDef]]] - - def rebuild(orig: TypeDef, newBody: List[Tree]): Tree = { - if (orig eq null) return EmptyTree - - val staticFields = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] - val newBodyWithStaticConstr = - if (staticFields.nonEmpty) { - /* do NOT put Flags.JavaStatic here. It breaks .enclosingClass */ - val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method | Flags.Private, MethodType(Nil, defn.UnitType)) - staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot)) - staticCostructor.entered - - val staticAssigns = staticFields.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor))) - tpd.DefDef(staticCostructor, Block(staticAssigns, tpd.unitLiteral)) :: newBody - } else newBody - - val oldTemplate = orig.rhs.asInstanceOf[Template] - cpy.TypeDef(orig)(rhs = cpy.Template(orig.rhs)(oldTemplate.constr, oldTemplate.parents, oldTemplate.self, newBodyWithStaticConstr)) - } - - def move(module: TypeDef, companion: TypeDef): List[Tree] = { - if (!module.symbol.is(Flags.Module)) move(companion, module) - else { - val allMembers = - (if(companion ne null) {companion.rhs.asInstanceOf[Template].body} else Nil) ++ - module.rhs.asInstanceOf[Template].body - val (newModuleBody, newCompanionBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == module.symbol}) - Trees.flatten(rebuild(companion, newCompanionBody) :: rebuild(module, newModuleBody) :: Nil) - } - } - val newPairs = - for ((name, classes) <- pairs) - yield - if (classes.tail.isEmpty) - if (classes.head.symbol.is(Flags.Module)) move(classes.head, null) - else List(rebuild(classes.head, classes.head.rhs.asInstanceOf[Template].body)) - else move(classes.head, classes.tail.head) - Trees.flatten(newPairs.toList.flatten ++ others) - } else trees - } -} diff --git a/src/dotty/tools/dotc/transform/NonLocalReturns.scala b/src/dotty/tools/dotc/transform/NonLocalReturns.scala deleted file mode 100644 index 7680e283e..000000000 --- a/src/dotty/tools/dotc/transform/NonLocalReturns.scala +++ /dev/null @@ -1,92 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Contexts._, Symbols._, Types._, Flags._, Decorators._, StdNames._, Constants._, Phases._ -import TreeTransforms._ -import ast.Trees._ -import collection.mutable - -object NonLocalReturns { - import ast.tpd._ - def isNonLocalReturn(ret: Return)(implicit ctx: Context) = - ret.from.symbol != ctx.owner.enclosingMethod || ctx.owner.is(Lazy) -} - -/** Implement non-local returns using NonLocalReturnControl exceptions. - */ -class NonLocalReturns extends MiniPhaseTransform { thisTransformer => - override def phaseName = "nonLocalReturns" - - import NonLocalReturns._ - import ast.tpd._ - - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimByName]) - - private def ensureConforms(tree: Tree, pt: Type)(implicit ctx: Context) = - if (tree.tpe <:< pt) tree - else Erasure.Boxing.adaptToType(tree, pt) - - /** The type of a non-local return expression with given argument type */ - private def nonLocalReturnExceptionType(argtype: Type)(implicit ctx: Context) = - defn.NonLocalReturnControlType.appliedTo(argtype) - - /** A hashmap from method symbols to non-local return keys */ - private val nonLocalReturnKeys = mutable.Map[Symbol, TermSymbol]() - - /** Return non-local return key for given method */ - private def nonLocalReturnKey(meth: Symbol)(implicit ctx: Context) = - nonLocalReturnKeys.getOrElseUpdate(meth, - ctx.newSymbol( - meth, ctx.freshName("nonLocalReturnKey").toTermName, Synthetic, defn.ObjectType, coord = meth.pos)) - - /** Generate a non-local return throw with given return expression from given method. - * I.e. for the method's non-local return key, generate: - * - * throw new NonLocalReturnControl(key, expr) - * todo: maybe clone a pre-existing exception instead? - * (but what to do about exceptions that miss their targets?) - */ - private def nonLocalReturnThrow(expr: Tree, meth: Symbol)(implicit ctx: Context) = - Throw( - New( - defn.NonLocalReturnControlType, - ref(nonLocalReturnKey(meth)) :: expr.ensureConforms(defn.ObjectType) :: Nil)) - - /** Transform (body, key) to: - * - * { - * val key = new Object() - * try { - * body - * } catch { - * case ex: NonLocalReturnControl => - * if (ex.key().eq(key)) ex.value().asInstanceOf[T] - * else throw ex - * } - * } - */ - private def nonLocalReturnTry(body: Tree, key: TermSymbol, meth: Symbol)(implicit ctx: Context) = { - val keyDef = ValDef(key, New(defn.ObjectType, Nil)) - val nonLocalReturnControl = defn.NonLocalReturnControlType - val ex = ctx.newSymbol(meth, nme.ex, EmptyFlags, nonLocalReturnControl, coord = body.pos) - val pat = BindTyped(ex, nonLocalReturnControl) - val rhs = If( - ref(ex).select(nme.key).appliedToNone.select(nme.eq).appliedTo(ref(key)), - ref(ex).select(nme.value).ensureConforms(meth.info.finalResultType), - Throw(ref(ex))) - val catches = CaseDef(pat, EmptyTree, rhs) :: Nil - val tryCatch = Try(body, catches, EmptyTree) - Block(keyDef :: Nil, tryCatch) - } - - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = - nonLocalReturnKeys.remove(tree.symbol) match { - case Some(key) => cpy.DefDef(tree)(rhs = nonLocalReturnTry(tree.rhs, key, tree.symbol)) - case _ => tree - } - - override def transformReturn(tree: Return)(implicit ctx: Context, info: TransformerInfo): Tree = - if (isNonLocalReturn(tree)) nonLocalReturnThrow(tree.expr, tree.from.symbol).withPos(tree.pos) - else tree -} diff --git a/src/dotty/tools/dotc/transform/NormalizeFlags.scala b/src/dotty/tools/dotc/transform/NormalizeFlags.scala deleted file mode 100644 index 755846904..000000000 --- a/src/dotty/tools/dotc/transform/NormalizeFlags.scala +++ /dev/null @@ -1,25 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.SymTransformer -import Phases.Phase -import Contexts.Context -import SymDenotations.SymDenotation -import TreeTransforms.MiniPhaseTransform -import Flags._, Symbols._ - -/** 1. Widens all private[this] and protected[this] qualifiers to just private/protected - * 2. Sets PureInterface flag for traits that only have pure interface members and that - * do not have initialization code. A pure interface member is either an abstract - * or alias type definition or a deferred val or def. - */ -class NormalizeFlags extends MiniPhaseTransform with SymTransformer { thisTransformer => - override def phaseName = "normalizeFlags" - - def transformSym(ref: SymDenotation)(implicit ctx: Context) = { - var newFlags = ref.flags &~ Local - if (newFlags != ref.flags) ref.copySymDenotation(initFlags = newFlags) - else ref - } -} diff --git a/src/dotty/tools/dotc/transform/OverridingPairs.scala b/src/dotty/tools/dotc/transform/OverridingPairs.scala deleted file mode 100644 index 650a03054..000000000 --- a/src/dotty/tools/dotc/transform/OverridingPairs.scala +++ /dev/null @@ -1,140 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Flags._, Symbols._, Contexts._, Types._, Scopes._, Decorators._ -import util.HashSet -import collection.mutable -import collection.immutable.BitSet -import scala.annotation.tailrec - -/** A module that can produce a kind of iterator (`Cursor`), - * which yields all pairs of overriding/overridden symbols - * that are visible in some baseclass, unless there's a parent class - * that already contains the same pairs. - * - * Adapted from the 2.9 version of OverridingPairs. The 2.10 version is IMO - * way too unwieldy to be maintained. - */ -object OverridingPairs { - - /** The cursor class - * @param base the base class that contains the overriding pairs - */ - class Cursor(base: Symbol)(implicit ctx: Context) { - - private val self = base.thisType - - /** Symbols to exclude: Here these are constructors and private locals. - * But it may be refined in subclasses. - */ - protected def exclude(sym: Symbol): Boolean = !sym.memberCanMatchInheritedSymbols - - /** The parents of base (may also be refined). - */ - protected def parents: Array[Symbol] = base.info.parents.toArray map (_.typeSymbol) - - /** Does `sym1` match `sym2` so that it qualifies as overriding. - * Types always match. Term symbols match if their membertypes - * relative to <base>.this do - */ - protected def matches(sym1: Symbol, sym2: Symbol): Boolean = - sym1.isType || self.memberInfo(sym1).matches(self.memberInfo(sym2)) - - /** The symbols that can take part in an overriding pair */ - private val decls = { - val decls = newScope - // fill `decls` with overriding shadowing overridden */ - def fillDecls(bcs: List[Symbol], deferred: Boolean): Unit = bcs match { - case bc :: bcs1 => - fillDecls(bcs1, deferred) - var e = bc.info.decls.lastEntry - while (e != null) { - if (e.sym.is(Deferred) == deferred && !exclude(e.sym)) - decls.enter(e.sym) - e = e.prev - } - case nil => - } - // first, deferred (this will need to change if we change lookup rules! - fillDecls(base.info.baseClasses, deferred = true) - // then, concrete. - fillDecls(base.info.baseClasses, deferred = false) - decls - } - - private val subParents = { - val subParents = new mutable.HashMap[Symbol, BitSet] - for (bc <- base.info.baseClasses) - subParents(bc) = BitSet(parents.indices.filter(parents(_).derivesFrom(bc)): _*) - subParents - } - - private def hasCommonParentAsSubclass(cls1: Symbol, cls2: Symbol): Boolean = - (subParents(cls1) intersect subParents(cls2)).nonEmpty - - /** The scope entries that have already been visited as overridden - * (maybe excluded because of hasCommonParentAsSubclass). - * These will not appear as overriding - */ - private val visited = new mutable.HashSet[Symbol] - - /** The current entry candidate for overriding - */ - private var curEntry = decls.lastEntry - - /** The current entry candidate for overridden */ - private var nextEntry = curEntry - - /** The current candidate symbol for overriding */ - var overriding: Symbol = _ - - /** If not null: The symbol overridden by overriding */ - var overridden: Symbol = _ - - //@M: note that next is called once during object initialization - final def hasNext: Boolean = nextEntry ne null - - /** @post - * curEntry = the next candidate that may override something else - * nextEntry = curEntry - * overriding = curEntry.sym - */ - private def nextOverriding(): Unit = { - @tailrec def loop(): Unit = - if (curEntry ne null) { - overriding = curEntry.sym - if (visited.contains(overriding)) { - curEntry = curEntry.prev - loop() - } - } - loop() - nextEntry = curEntry - } - - /** @post - * hasNext = there is another overriding pair - * overriding = overriding member of the pair, provided hasNext is true - * overridden = overridden member of the pair, provided hasNext is true - */ - @tailrec final def next(): Unit = - if (nextEntry ne null) { - nextEntry = decls.lookupNextEntry(nextEntry) - if (nextEntry ne null) { - overridden = nextEntry.sym - if (overriding.owner != overridden.owner && matches(overriding, overridden)) { - visited += overridden - if (!hasCommonParentAsSubclass(overriding.owner, overridden.owner)) return - } - } else { - curEntry = curEntry.prev - nextOverriding() - } - next() - } - - nextOverriding() - next() - } -} diff --git a/src/dotty/tools/dotc/transform/ParamForwarding.scala b/src/dotty/tools/dotc/transform/ParamForwarding.scala deleted file mode 100644 index 9571c387b..000000000 --- a/src/dotty/tools/dotc/transform/ParamForwarding.scala +++ /dev/null @@ -1,94 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import ast.Trees._ -import Contexts._, Types._, Symbols._, Flags._, TypeUtils._, DenotTransformers._, StdNames._ - -/** For all parameter accessors - * - * val x: T = ... - * - * if - * (1) x is forwarded in the supercall to a parameter that's also named `x` - * (2) the superclass parameter accessor for `x` is accessible from the current class - * change the accessor to - * - * def x: T = super.x.asInstanceOf[T] - * - * Do the same also if there are intermediate inaccessible parameter accessor forwarders. - * The aim of this transformation is to avoid redundant parameter accessor fields. - */ -class ParamForwarding(thisTransformer: DenotTransformer) { - import ast.tpd._ - - def forwardParamAccessors(impl: Template)(implicit ctx: Context): Template = { - def fwd(stats: List[Tree])(implicit ctx: Context): List[Tree] = { - val (superArgs, superParamNames) = impl.parents match { - case superCall @ Apply(fn, args) :: _ => - fn.tpe.widen match { - case MethodType(paramNames, _) => (args, paramNames) - case _ => (Nil, Nil) - } - case _ => (Nil, Nil) - } - def inheritedAccessor(sym: Symbol): Symbol = { - /** - * Dmitry: having it have the same name is needed to maintain correctness in presence of subclassing - * if you would use parent param-name `a` to implement param-field `b` - * overriding field `b` will actually override field `a`, that is wrong! - * - * class A(val s: Int); - * class B(val b: Int) extends A(b) - * class C extends A(2) { - * def s = 3 - * assert(this.b == 2) - * } - */ - val candidate = sym.owner.asClass.superClass - .info.decl(sym.name).suchThat(_ is (ParamAccessor, butNot = Mutable)).symbol - if (candidate.isAccessibleFrom(currentClass.thisType, superAccess = true)) candidate - else if (candidate is Method) inheritedAccessor(candidate) - else NoSymbol - } - def forwardParamAccessor(stat: Tree): Tree = { - stat match { - case stat: ValDef => - val sym = stat.symbol.asTerm - if (sym is (ParamAccessor, butNot = Mutable)) { - val idx = superArgs.indexWhere(_.symbol == sym) - if (idx >= 0 && superParamNames(idx) == stat.name) { // supercall to like-named parameter - val alias = inheritedAccessor(sym) - if (alias.exists) { - def forwarder(implicit ctx: Context) = { - sym.copySymDenotation(initFlags = sym.flags | Method | Stable, info = sym.info.ensureMethodic) - .installAfter(thisTransformer) - val superAcc = - Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) - DefDef(sym, superAcc.ensureConforms(sym.info.widen)) - } - return forwarder(ctx.withPhase(thisTransformer.next)) - } - } - } - case _ => - } - stat - } - stats map forwardParamAccessor - } - - cpy.Template(impl)(body = fwd(impl.body)(ctx.withPhase(thisTransformer))) - } - - def adaptRef[T <: RefTree](tree: T)(implicit ctx: Context): T = tree.tpe match { - case tpe: TermRefWithSignature - if tpe.sig == Signature.NotAMethod && tpe.symbol.is(Method) => - // It's a param forwarder; adapt the signature - tree.withType( - TermRef.withSig(tpe.prefix, tpe.name, tpe.prefix.memberInfo(tpe.symbol).signature)) - .asInstanceOf[T] - case _ => - tree - } -} diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala deleted file mode 100644 index 3e25cf82e..000000000 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ /dev/null @@ -1,1989 +0,0 @@ -package dotty.tools.dotc -package transform - -import scala.language.postfixOps - -import TreeTransforms._ -import core.Denotations._ -import core.SymDenotations._ -import core.Contexts._ -import core.Symbols._ -import core.Types._ -import core.Constants._ -import core.StdNames._ -import dotty.tools.dotc.ast.{untpd, TreeTypeMap, tpd} -import dotty.tools.dotc.core -import dotty.tools.dotc.core.DenotTransformers.DenotTransformer -import dotty.tools.dotc.core.Phases.Phase -import dotty.tools.dotc.core.{TypeApplications, Flags} -import dotty.tools.dotc.typer.Applications -import dotty.tools.dotc.util.Positions -import typer.ErrorReporting._ -import ast.Trees._ -import Applications._ -import TypeApplications._ -import SymUtils._, core.NameOps._ -import core.Mode -import patmat._ - -import dotty.tools.dotc.util.Positions.Position -import dotty.tools.dotc.core.Decorators._ -import dotty.tools.dotc.core.Flags - -import scala.reflect.internal.util.Collections - -/** This transform eliminates patterns. Right now it's a dummy. - * Awaiting the real pattern matcher. - * elimRepeated is required - * TODO: outer tests are not generated yet. - */ -class PatternMatcher extends MiniPhaseTransform with DenotTransformer { - import dotty.tools.dotc.ast.tpd._ - - override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref - - override def runsAfter = Set(classOf[ElimRepeated]) - - override def runsAfterGroupsOf = Set(classOf[TailRec]) // tailrec is not capable of reversing the patmat tranformation made for tree - - override def phaseName = "patternMatcher" - - private var _id = 0 // left for debuging - - override def transformMatch(tree: Match)(implicit ctx: Context, info: TransformerInfo): Tree = { - val translated = new Translator()(ctx).translator.translateMatch(tree) - - // check exhaustivity and unreachability - val engine = new SpaceEngine - if (engine.checkable(tree)) { - engine.checkExhaustivity(tree) - engine.checkRedundancy(tree) - } - - translated.ensureConforms(tree.tpe) - } - - class Translator(implicit ctx: Context) { - - def translator = { - new OptimizingMatchTranslator/*(localTyper)*/ - } - - class OptimizingMatchTranslator extends MatchOptimizer/*(val typer: analyzer.Typer)*/ with MatchTranslator - - trait CodegenCore { - private var ctr = 0 // left for debugging - - // assert(owner ne null); assert(owner ne NoSymbol) - def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x", owner: Symbol = ctx.owner) = { - ctr += 1 - ctx.newSymbol(owner, ctx.freshName(prefix + ctr).toTermName, Flags.Synthetic | Flags.Case, tp, coord = pos) - } - - def newSynthCaseLabel(name: String, tpe: Type, owner: Symbol = ctx.owner) = - ctx.newSymbol(owner, ctx.freshName(name).toTermName, Flags.Label | Flags.Synthetic | Flags.Method, tpe).asTerm - //NoSymbol.newLabel(freshName(name), NoPosition) setFlag treeInfo.SYNTH_CASE_FLAGS - - // codegen relevant to the structure of the translation (how extractors are combined) - trait AbsCodegen { - def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Symbol => Tree]): Tree - - // local / context-free - - /* cast b to tp */ - def _asInstanceOf(b: Symbol, tp: Type): Tree - /* a check `checker` == binder */ - def _equals(checker: Tree, binder: Symbol): Tree - /* b.isIsInstanceOf[tp] */ - def _isInstanceOf(b: Symbol, tp: Type): Tree - /* tgt is expected to be a Seq, call tgt.drop(n) */ - def drop(tgt: Tree)(n: Int): Tree - /* tgt is expected to have method apply(int), call tgt.drop(i) */ - def index(tgt: Tree)(i: Int): Tree - /* make tree that accesses the i'th component of the tuple referenced by binder */ - def tupleSel(binder: Symbol)(i: Int): Tree - } - - // structure - trait Casegen extends AbsCodegen { - def one(res: Tree): Tree - - def flatMap(prev: Tree, b: Symbol, next: Tree): Tree - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree - def flatMapGuard(cond: Tree, next: Tree): Tree - def ifThenElseZero(c: Tree, thenp: Tree): Tree = - If(c, thenp, zero) - protected def zero: Tree - } - - def codegen: AbsCodegen - - abstract class CommonCodegen extends AbsCodegen { - def tupleSel(binder: Symbol)(i: Int): Tree = ref(binder).select(nme.productAccessorName(i)) - def index(tgt: Tree)(i: Int): Tree = { - if (i > 0) tgt.select(defn.Seq_apply).appliedTo(Literal(Constant(i))) - else tgt.select(defn.Seq_head).ensureApplied - } - - // Right now this blindly calls drop on the result of the unapplySeq - // unless it verifiably has no drop method (this is the case in particular - // with Array.) You should not actually have to write a method called drop - // for name-based matching, but this was an expedient route for the basics. - def drop(tgt: Tree)(n: Int): Tree = { - def callDirect = tgt.select(nme.drop).appliedTo(Literal(Constant(n))) - def callRuntime = ref(defn.ScalaRuntime_drop).appliedTo(tgt, Literal(Constant(n))) - - def needsRuntime = !(tgt.tpe derivesFrom defn.SeqClass) /*typeOfMemberNamedDrop(tgt.tpe) == NoType*/ - - if (needsRuntime) callRuntime else callDirect - } - - // NOTE: checker must be the target of the ==, that's the patmat semantics for ya - def _equals(checker: Tree, binder: Symbol): Tree = - tpd.applyOverloaded(checker, nme.EQ, List(ref(binder)), List.empty, defn.BooleanType) - - // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) - def _asInstanceOf(b: Symbol, tp: Type): Tree = ref(b).ensureConforms(tp) // andType here breaks t1048 - def _isInstanceOf(b: Symbol, tp: Type): Tree = ref(b).select(defn.Any_isInstanceOf).appliedToType(tp) - } - } - - object Rebindings { - def apply(from: Symbol, to: Symbol) = new Rebindings(List(from), List(ref(to))) - // requires sameLength(from, to) - def apply(from: List[Symbol], to: List[Tree]) = - if (from nonEmpty) new Rebindings(from, to) else NoRebindings - } - - class Rebindings(val lhs: List[Symbol], val rhs: List[Tree]) { - def >>(other: Rebindings) = { - if (other eq NoRebindings) this - else if (this eq NoRebindings) other - else { - assert((lhs.toSet ++ other.lhs.toSet).size == lhs.length + other.lhs.length, "no double assignments") - new Rebindings(this.lhs ++ other.lhs, this.rhs ++ other.rhs) - } - } - - def emitValDefs: List[ValDef] = { - Collections.map2(lhs, rhs)((symbol, tree) => ValDef(symbol.asTerm, tree.ensureConforms(symbol.info))) - } - } - object NoRebindings extends Rebindings(Nil, Nil) - - trait OptimizedCodegen extends CodegenCore { - override def codegen: AbsCodegen = optimizedCodegen - - // when we know we're targetting Option, do some inlining the optimizer won't do - // for example, `o.flatMap(f)` becomes `if (o == None) None else f(o.get)`, similarly for orElse and guard - // this is a special instance of the advanced inlining optimization that takes a method call on - // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases - object optimizedCodegen extends CommonCodegen { - - /** Inline runOrElse and get rid of Option allocations - * - * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse ${catchAll(`scrut`)} - * the matcher's optional result is encoded as a flag, keepGoing, where keepGoing == true encodes result.isEmpty, - * if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x - */ - def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Symbol => Tree]): Tree = { - //val matchRes = ctx.newSymbol(NoSymbol, ctx.freshName("matchRes").toTermName, Flags.Synthetic | Flags.Param | Flags.Label | Flags.Method, restpe /*withoutAnnotations*/) - //NoSymbol.newValueParameter(newTermName("x"), NoPosition, newFlags = SYNTHETIC) setInfo restpe.withoutAnnotations - - - val caseSyms: List[TermSymbol] = cases.scanLeft(ctx.owner.asTerm)((curOwner, nextTree) => newSynthCaseLabel(ctx.freshName("case"), MethodType(Nil, restpe), curOwner)).tail - - // must compute catchAll after caseLabels (side-effects nextCase) - // catchAll.isEmpty iff no synthetic default case needed (the (last) user-defined case is a default) - // if the last user-defined case is a default, it will never jump to the next case; it will go immediately to matchEnd - val catchAllDef = matchFailGen.map { _(scrutSym) } - .getOrElse(Throw(New(defn.MatchErrorType, List(ref(scrutSym))))) - - val matchFail = newSynthCaseLabel(ctx.freshName("matchFail"), MethodType(Nil, restpe)) - val catchAllDefBody = DefDef(matchFail, catchAllDef) - - val nextCases = (caseSyms.tail ::: List(matchFail)).map(ref(_).ensureApplied) - val caseDefs = (cases zip caseSyms zip nextCases).foldRight[Tree](catchAllDefBody) { - // dotty deviation - //case (((mkCase, sym), nextCase), acc) => - (x: (((Casegen => Tree), TermSymbol), Tree), acc: Tree) => x match { - case ((mkCase, sym), nextCase) => - val body = mkCase(new OptimizedCasegen(nextCase)).ensureConforms(restpe) - - DefDef(sym, _ => Block(List(acc), body)) - } - } - - // scrutSym == NoSymbol when generating an alternatives matcher - // val scrutDef = scrutSym.fold(List[Tree]())(ValDef(_, scrut) :: Nil) // for alternatives - - Block(List(caseDefs), ref(caseSyms.head).ensureApplied) - } - - class OptimizedCasegen(nextCase: Tree) extends CommonCodegen with Casegen { - def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Symbol => Tree]): Tree = - optimizedCodegen.matcher(scrut, scrutSym, restpe)(cases, matchFailGen) - - // only used to wrap the RHS of a body - // res: T - // returns MatchMonad[T] - def one(res: Tree): Tree = /*ref(matchEnd) appliedTo*/ res // a jump to a case label is special-cased in typedApply - protected def zero: Tree = nextCase - - // prev: MatchMonad[T] - // b: T - // next: MatchMonad[U] - // returns MatchMonad[U] - def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { - - val getTp = extractorMemberType(prev.tpe, nme.get) - val isDefined = extractorMemberType(prev.tpe, nme.isDefined) - - if ((isDefined isRef defn.BooleanClass) && getTp.exists) { - // isDefined and get may be overloaded - val getDenot = prev.tpe.member(nme.get).suchThat(_.info.isParameterless) - val isDefinedDenot = prev.tpe.member(nme.isDefined).suchThat(_.info.isParameterless) - - val tmpSym = freshSym(prev.pos, prev.tpe, "o") - val prevValue = ref(tmpSym).select(getDenot.symbol).ensureApplied - - Block( - List(ValDef(tmpSym, prev)), - // must be isEmpty and get as we don't control the target of the call (prev is an extractor call) - ifThenElseZero( - ref(tmpSym).select(isDefinedDenot.symbol), - Block(List(ValDef(b.asTerm, prevValue)), next) - ) - ) - } else { - assert(defn.isProductSubType(prev.tpe)) - val nullCheck: Tree = prev.select(defn.Object_ne).appliedTo(Literal(Constant(null))) - ifThenElseZero( - nullCheck, - Block( - List(ValDef(b.asTerm, prev)), - next //Substitution(b, ref(prevSym))(next) - ) - ) - } - } - - // cond: Boolean - // res: T - // nextBinder: T - // next == MatchMonad[U] - // returns MatchMonad[U] - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = { - val rest = Block(List(ValDef(nextBinder.asTerm, res)), next) - ifThenElseZero(cond, rest) - } - - // guardTree: Boolean - // next: MatchMonad[T] - // returns MatchMonad[T] - def flatMapGuard(guardTree: Tree, next: Tree): Tree = - ifThenElseZero(guardTree, next) - - def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = - ifThenElseZero(cond, Block( - List(Assign(ref(condSym), Literal(Constant(true))), - Assign(ref(nextBinder), res)), - next - )) - } - } - } - final case class Suppression(exhaustive: Boolean, unreachable: Boolean) - object Suppression { - val NoSuppression = Suppression(false, false) - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // the making of the trees - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - trait TreeMakers extends CodegenCore { - def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) - def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, suppression: Suppression): Unit = {} - - def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Symbol => Tree], unchecked: Boolean): Option[Tree] = { - // TODO Deal with guards? - - def isSwitchableType(tpe: Type): Boolean = - (tpe isRef defn.IntClass) || - (tpe isRef defn.ByteClass) || - (tpe isRef defn.ShortClass) || - (tpe isRef defn.CharClass) - - object IntEqualityTestTreeMaker { - def unapply(treeMaker: EqualityTestTreeMaker): Option[Int] = treeMaker match { - case EqualityTestTreeMaker(`scrutSym`, _, Literal(const), _) => - if (const.isIntRange) Some(const.intValue) - else None - case _ => - None - } - } - - def isSwitchCase(treeMakers: List[TreeMaker]): Boolean = treeMakers match { - // case 5 => - case List(IntEqualityTestTreeMaker(_), _: BodyTreeMaker) => - true - - // case 5 | 6 => - case List(AlternativesTreeMaker(`scrutSym`, alts, _), _: BodyTreeMaker) => - alts.forall { - case List(IntEqualityTestTreeMaker(_)) => true - case _ => false - } - - // case _ => - case List(_: BodyTreeMaker) => - true - - /* case x @ pat => - * This includes: - * case x => - * case x @ 5 => - * case x @ (5 | 6) => - */ - case (_: SubstOnlyTreeMaker) :: rest => - isSwitchCase(rest) - - case _ => - false - } - - /* (Nil, body) means that `body` is the default case - * It's a bit hacky but it simplifies manipulations. - */ - def extractSwitchCase(treeMakers: List[TreeMaker]): (List[Int], BodyTreeMaker) = treeMakers match { - // case 5 => - case List(IntEqualityTestTreeMaker(intValue), body: BodyTreeMaker) => - (List(intValue), body) - - // case 5 | 6 => - case List(AlternativesTreeMaker(_, alts, _), body: BodyTreeMaker) => - val intValues = alts.map { - case List(IntEqualityTestTreeMaker(intValue)) => intValue - } - (intValues, body) - - // case _ => - case List(body: BodyTreeMaker) => - (Nil, body) - - // case x @ pat => - case (_: SubstOnlyTreeMaker) :: rest => - /* Rebindings have been propagated, so the eventual body in `rest` - * contains all the necessary information. The substitution can be - * dropped at this point. - */ - extractSwitchCase(rest) - } - - def doOverlap(a: List[Int], b: List[Int]): Boolean = - a.exists(b.contains _) - - def makeSwitch(valuesToCases: List[(List[Int], BodyTreeMaker)]): Tree = { - def genBody(body: BodyTreeMaker): Tree = { - val valDefs = body.rebindings.emitValDefs - if (valDefs.isEmpty) body.body - else Block(valDefs, body.body) - } - - val intScrut = - if (pt isRef defn.IntClass) ref(scrutSym) - else Select(ref(scrutSym), nme.toInt) - - val (normalCases, defaultCaseAndRest) = valuesToCases.span(_._1.nonEmpty) - - val newCases = for { - (values, body) <- normalCases - } yield { - val literals = values.map(v => Literal(Constant(v))) - val pat = - if (literals.size == 1) literals.head - else Alternative(literals) - CaseDef(pat, EmptyTree, genBody(body)) - } - - val catchAllDef = { - if (defaultCaseAndRest.isEmpty) { - matchFailGenOverride.fold[Tree]( - Throw(New(defn.MatchErrorType, List(ref(scrutSym)))))( - _(scrutSym)) - } else { - /* After the default case, assuming the IR even allows anything, - * things are unreachable anyway and can be removed. - */ - genBody(defaultCaseAndRest.head._2) - } - } - val defaultCase = CaseDef(Underscore(defn.IntType), EmptyTree, catchAllDef) - - Match(intScrut, newCases :+ defaultCase) - } - - val dealiased = scrut.tpe.widenDealias - if (isSwitchableType(dealiased) && cases.forall(isSwitchCase)) { - val valuesToCases = cases.map(extractSwitchCase) - val values = valuesToCases.map(_._1) - if (values.tails.exists { tail => tail.nonEmpty && tail.tail.exists(doOverlap(_, tail.head)) }) { - // TODO Deal with overlapping cases (mostly useless without guards) - None - } else { - Some(makeSwitch(valuesToCases)) - } - } else { - if (dealiased hasAnnotation defn.SwitchAnnot) - ctx.warning("failed to emit switch for `@switch` annotated match", scrut.pos) - None - } - } - - // for catch (no need to customize match failure) - def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = - None // todo - - abstract class TreeMaker { - def pos: Position - - private[this] var currSub: Rebindings = null - - /** captures the scope and the value of the bindings in patterns - * important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed) - */ - def rebindings: Rebindings = - if (currSub eq null) introducedRebindings - else currSub - - protected def introducedRebindings: Rebindings - - private[TreeMakers] def incorporateOuterRebinding(outerSubst: Rebindings): Unit = { - if (currSub ne null) { - ctx.debuglog("BUG: incorporateOuterRebinding called more than once for " + ((this, currSub, outerSubst))) - if (ctx.debug) Thread.dumpStack() - } - else currSub = outerSubst >> rebindings - } - - /** The substitution that specifies the trees that compute the values of the subpattern binders. - * - * Should not be used to perform actual substitution! - * Only used to reason symbolically about the values the subpattern binders are bound to. - * See TreeMakerToCond#updateSubstitution. - * - * Overridden in PreserveSubPatBinders to pretend it replaces the subpattern binders by subpattern refs - * (Even though we don't do so anymore -- see SI-5158, SI-5739 and SI-6070.) - * - * TODO: clean this up, would be nicer to have some higher-level way to compute - * the binders bound by this tree maker and the symbolic values that correspond to them - */ - def subPatternsAsRebindings: Rebindings = rebindings - - // build Tree that chains `next` after the current extractor - def chainBefore(next: Tree)(casegen: Casegen): Tree - } - - sealed trait NoNewBinders extends TreeMaker { - protected val introducedRebindings: Rebindings = NoRebindings - } - - case class TrivialTreeMaker(tree: Tree) extends TreeMaker with NoNewBinders { - def pos = tree.pos - - def chainBefore(next: Tree)(casegen: Casegen): Tree = tree - } - - case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker with NoNewBinders { - def pos = body.pos - - def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(next eq EmptyTree) - /*atPos(body.pos)*/(casegen.one(body)) // since SubstOnly treemakers are dropped, need to do it here - override def toString = "B" + ((body, matchPt)) - } - - /** - * In scalac for such block - * x match { - * case d => <body> - * } - * - * d inside <body> was to be substitued by x. - * - * In dotty, SubstOnlyTreeMakers instead generate normal ValDef, - * and does not create a new substitution. - * - * This was done for several reasons: - * 1) it is a lot easyer to Y-check, - * as d type could be used in <body>. - * 2) it would simplify debugging of the generated code as - * this works also for nested patterns, and previously they used unreadable names - * 3) It showed better(~30%), performance, - * Rebuilding tree and propagating types was taking substantial time. - */ - case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker { - val pos = Positions.NoPosition - - val introducedRebindings = Rebindings(prevBinder, nextBinder) - def chainBefore(next: Tree)(casegen: Casegen): Tree = next - //override def toString = "S" + localSubstitution - } - - sealed abstract class FunTreeMaker extends TreeMaker { - val nextBinder: Symbol - def pos = nextBinder.pos - } - - sealed abstract class CondTreeMaker extends FunTreeMaker { - val prevBinder: Symbol - val nextBinderTp: Type - val cond: Tree - val res: Tree - - val nextBinder: Symbol - lazy val introducedRebindings = /* - if (nextBinder ne prevBinder) Rebindings(prevBinder, nextBinder) - else */ NoRebindings - - def chainBefore(next: Tree)(casegen: Casegen): Tree = - if (prevBinder ne nextBinder) // happens when typeTest is known to succeed - /*atPos(pos)(*/casegen.flatMapCond(cond, res, nextBinder, next)//) - else casegen.flatMapGuard(cond, next) - } - - // unless we're optimizing, emit local variable bindings for all subpatterns of extractor/case class patterns - protected val debugInfoEmitVars = true //!settings.optimise.value - - /** - * Tree maker that captures sub pattern values during pattern match. - */ - sealed trait PreserveSubPatBinders extends TreeMaker { - val subPatBinders: List[Symbol] // captured values - val subPatRefs: List[Tree] // trees that will replace references to subPatBinders - val ignoredSubPatBinders: Set[Symbol] // ignored as they aren't used in body of pattern - - // unless `debugInfoEmitVars`, this set should contain the bare minimum for correctness - // mutable case class fields need to be stored regardless (SI-5158, SI-6070) -- see override in ProductExtractorTreeMaker - // sub patterns bound to wildcard (_) are never stored as they can't be referenced - // dirty debuggers will have to get dirty to see the wildcards - lazy val storedBinders: Set[Symbol] = - (if (debugInfoEmitVars) subPatBinders.toSet else Set.empty) ++ extraStoredBinders -- ignoredSubPatBinders - - // e.g., mutable fields of a case class in ProductExtractorTreeMaker - def extraStoredBinders: Set[Symbol] - - def emitVars = storedBinders.nonEmpty - - lazy val storedSubsted = (subPatBinders, subPatRefs).zipped.partition{ case (sym, _) => storedBinders(sym) } - - def stored = storedSubsted._1 - - def substed = storedSubsted._2 - - // dd: this didn't yet trigger error. But I believe it would. if this causes double denition of symbol error this can be replaced with NoRebindings - protected lazy val introducedRebindings: Rebindings = if (!emitVars) Rebindings(subPatBinders, subPatRefs) - else { - val (subPatBindersSubstituted, subPatRefsSubstituted) = substed.unzip - Rebindings(subPatBindersSubstituted.toList, subPatRefsSubstituted.toList) - } - - /** The substitution that specifies the trees that compute the values of the subpattern binders. - * - * We pretend to replace the subpattern binders by subpattern refs - * (Even though we don't do so anymore -- see SI-5158, SI-5739 and SI-6070.) - */ - override def subPatternsAsRebindings = - Rebindings(subPatBinders, subPatRefs) >> super.subPatternsAsRebindings - - def bindSubPats(in: Tree): Tree = - if (!emitVars) in - else { - // binders in `subPatBindersStored` that are referenced by tree `in` - val usedBinders = new collection.mutable.HashSet[Symbol]() - // all potentially stored subpat binders - val potentiallyStoredBinders = stored.unzip._1.toSet - // compute intersection of all symbols in the tree `in` and all potentially stored subpat binders - new DeepFolder[Unit]((x: Unit, t: Tree) => - if (potentiallyStoredBinders(t.symbol)) usedBinders += t.symbol).apply((), in) - - if (usedBinders.isEmpty) in - else { - // only store binders actually used - val (subPatBindersStored, subPatRefsStored) = stored.filter{case (b, _) => usedBinders(b)}.unzip - - Block(Collections.map2(subPatBindersStored.toList, subPatRefsStored.toList)((bind, ref) => { - // required in case original pattern had a more precise type - // eg case s@"foo" => would be otherwise translated to s with type String instead of String("foo") - def refTpeWiden = ref.tpe.widen - def bindInfoWiden = bind.info.widen - def loc = bind.showFullName - if (!(ref.tpe <:< bind.info.widen)) { - ctx.debuglog(s"here ${bind.showFullName} expected: ${bindInfoWiden.show} got: ${refTpeWiden.show}") - } - val refCasted = ref.ensureConforms(bind.info) - ValDef(bind.asTerm, refCasted) - }), in) - } - } - } - - /** - * Make a TreeMaker that will result in an extractor call specified by `extractor` - * the next TreeMaker (here, we don't know which it'll be) is chained after this one by flatMap'ing - * a function with binder `nextBinder` over our extractor's result - * the function's body is determined by the next TreeMaker - * (furthermore, the interpretation of `flatMap` depends on the codegen instance we're using). - * - * The values for the subpatterns, as computed by the extractor call in `extractor`, - * are stored in local variables that re-use the symbols in `subPatBinders`. - * This makes extractor patterns more debuggable (SI-5739). - */ - case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol)( - val subPatBinders: List[Symbol], - val subPatRefs: List[Tree], - extractorReturnsBoolean: Boolean, - val checkedLength: Option[Int], - val prevBinder: Symbol, - val ignoredSubPatBinders: Set[Symbol] - ) extends FunTreeMaker with PreserveSubPatBinders { - - def extraStoredBinders: Set[Symbol] = Set() - - ctx.debuglog(s""" - |ExtractorTreeMaker($extractor, $extraCond, $nextBinder) { - | $subPatBinders - | $subPatRefs - | $extractorReturnsBoolean - | $checkedLength - | $prevBinder - | $ignoredSubPatBinders - |}""".stripMargin) - - def chainBefore(next: Tree)(casegen: Casegen): Tree = { - val condAndNext = extraCond match { - case Some(cond: Tree) => - casegen.ifThenElseZero(cond, bindSubPats(next)) - case _ => - bindSubPats(next) - } - - if (extractorReturnsBoolean) casegen.flatMapCond(extractor, unitLiteral, nextBinder, condAndNext) - else casegen.flatMap(extractor, nextBinder, condAndNext) // getType? - } - - override def toString = "X" + ((extractor, nextBinder.name)) - } - - /** - * An optimized version of ExtractorTreeMaker for Products. - * For now, this is hard-coded to case classes, and we simply extract the case class fields. - * - * The values for the subpatterns, as specified by the case class fields at the time of extraction, - * are stored in local variables that re-use the symbols in `subPatBinders`. - * This makes extractor patterns more debuggable (SI-5739) as well as - * avoiding mutation after the pattern has been matched (SI-5158, SI-6070) - * - * TODO: make this user-definable as follows - * When a companion object defines a method `def unapply_1(x: T): U_1`, but no `def unapply` or `def unapplySeq`, - * the extractor is considered to match any non-null value of type T - * the pattern is expected to have as many sub-patterns as there are `def unapply_I(x: T): U_I` methods, - * and the type of the I'th sub-pattern is `U_I`. - * The same exception for Seq patterns applies: if the last extractor is of type `Seq[U_N]`, - * the pattern must have at least N arguments (exactly N if the last argument is annotated with `: _*`). - * The arguments starting at N (and beyond) are taken from the sequence returned by apply_N, - * and it is checked that the sequence has enough elements to provide values for all expected sub-patterns. - * - * For a case class C, the implementation is assumed to be `def unapply_I(x: C) = x._I`, - * and the extractor call is inlined under that assumption. - */ - case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree])( - val subPatBinders: List[Symbol], - val subPatRefs: List[Tree], - val mutableBinders: List[Symbol], - binderKnownNonNull: Boolean, - val ignoredSubPatBinders: Set[Symbol] - ) extends FunTreeMaker with PreserveSubPatBinders { - - val nextBinder = prevBinder // just passing through - - // mutable binders must be stored to avoid unsoundness or seeing mutation of fields after matching (SI-5158, SI-6070) - def extraStoredBinders: Set[Symbol] = mutableBinders.toSet - - def chainBefore(next: Tree)(casegen: Casegen): Tree = { - val nullCheck: Tree = ref(prevBinder).select(defn.Object_ne).appliedTo(Literal(Constant(null))) - - val cond: Option[Tree] = - if (binderKnownNonNull) extraCond - else extraCond.map(nullCheck.select(defn.Boolean_&&).appliedTo).orElse(Some(nullCheck)) - - cond match { - case Some(cond: Tree) => - casegen.ifThenElseZero(cond, bindSubPats(next)) - case _ => - bindSubPats(next) - } - } - - override def toString = "P" + ((prevBinder.name, extraCond getOrElse "", introducedRebindings)) - } - - object IrrefutableExtractorTreeMaker { - // will an extractor with unapply method of methodtype `tp` always succeed? - // note: this assumes the other side-conditions implied by the extractor are met - // (argument of the right type, length check succeeds for unapplySeq,...) - def irrefutableExtractorType(tp: Type): Boolean = tp.resultType.dealias match { - // case TypeRef(_, SomeClass, _) => true todo - // probably not useful since this type won't be inferred nor can it be written down (yet) - // case ConstantTrue => true todo - case _ => false - } - - def unapply(xtm: ExtractorTreeMaker): Option[(Tree, Symbol)] = xtm match { - case ExtractorTreeMaker(extractor, None, nextBinder) if irrefutableExtractorType(extractor.tpe) => - Some((extractor, nextBinder)) - case _ => - None - } - } - - object TypeTestTreeMaker { - // factored out so that we can consistently generate other representations of the tree that implements the test - // (e.g. propositions for exhaustivity and friends, boolean for isPureTypeTest) - trait TypeTestCondStrategy { - type Result - - def outerTest(testedBinder: Symbol, expectedTp: Type): Result - // TODO: can probably always widen - def typeTest(testedBinder: Symbol, expectedTp: Type): Result - def nonNullTest(testedBinder: Symbol): Result - def equalsTest(pat: Tree, testedBinder: Symbol): Result - def eqTest(pat: Tree, testedBinder: Symbol): Result - def and(a: Result, b: Result): Result - def tru: Result - } - - object treeCondStrategy extends TypeTestCondStrategy { - type Result = Tree - - def and(a: Result, b: Result): Result = a.select(defn.Boolean_&&).appliedTo(b) - def tru = Literal(Constant(true)) - def typeTest(testedBinder: Symbol, expectedTp: Type) = codegen._isInstanceOf(testedBinder, expectedTp) - def nonNullTest(testedBinder: Symbol) = ref(testedBinder).select(defn.Object_ne).appliedTo(Literal(Constant(null))) - def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) - def eqTest(pat: Tree, testedBinder: Symbol) = ref(testedBinder).select(defn.Object_eq).appliedTo(pat) - - def outerTest(testedBinder: Symbol, expectedTp: Type): Tree = { - val expectedOuter = expectedTp.normalizedPrefix match { - //case NoType => Literal(Constant(true)) // fallback for SI-6183 todo? - case pre: SingletonType => singleton(pre) - } - - // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` - // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - // val outer = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedTp.prefix - - val expectedClass = expectedTp.dealias.classSymbol.asClass - val test = codegen._asInstanceOf(testedBinder, expectedTp) - // TODO: Use nme.OUTER_SELECT, like the Inliner does? - val outerAccessorTested = ctx.atPhase(ctx.explicitOuterPhase.next) { implicit ctx => - ExplicitOuter.ensureOuterAccessors(expectedClass) - test.select(ExplicitOuter.outerAccessor(expectedClass)).select(defn.Object_eq).appliedTo(expectedOuter) - } - outerAccessorTested - } - } - - /*object pureTypeTestChecker extends TypeTestCondStrategy { - type Result = Boolean - - def typeTest(testedBinder: Symbol, expectedTp: Type): Result = true - - def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false - def nonNullTest(testedBinder: Symbol): Result = false - def equalsTest(pat: Tree, testedBinder: Symbol): Result = false - def eqTest(pat: Tree, testedBinder: Symbol): Result = false - def and(a: Result, b: Result): Result = false // we don't and type tests, so the conjunction must include at least one false - def tru = true - }*/ - - def nonNullImpliedByTestChecker(binder: Symbol) = new TypeTestCondStrategy { - type Result = Boolean - - def typeTest(testedBinder: Symbol, expectedTp: Type): Result = testedBinder eq binder - def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false - def nonNullTest(testedBinder: Symbol): Result = testedBinder eq binder - def equalsTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null - def eqTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null - def and(a: Result, b: Result): Result = a || b - def tru = false - } - } - - /** implements the run-time aspects of (§8.2) (typedPattern has already done the necessary type transformations) - * - * Type patterns consist of types, type variables, and wildcards. A type pattern T is of one of the following forms: - - A reference to a class C, p.C, or T#C. - This type pattern matches any non-null instance of the given class. - Note that the prefix of the class, if it is given, is relevant for determining class instances. - For instance, the pattern p.C matches only instances of classes C which were created with the path p as prefix. - The bottom types scala.Nothing and scala.Null cannot be used as type patterns, because they would match nothing in any case. - - - A singleton type p.type. - This type pattern matches only the value denoted by the path p - (that is, a pattern match involved a comparison of the matched value with p using method eq in class AnyRef). // TODO: the actual pattern matcher uses ==, so that's what I'm using for now - // https://issues.scala-lang.org/browse/SI-4577 "pattern matcher, still disappointing us at equality time" - - - A compound type pattern T1 with ... with Tn where each Ti is a type pat- tern. - This type pattern matches all values that are matched by each of the type patterns Ti. - - - A parameterized type pattern T[a1,...,an], where the ai are type variable patterns or wildcards _. - This type pattern matches all values which match T for some arbitrary instantiation of the type variables and wildcards. - The bounds or alias type of these type variable are determined as described in (§8.3). - - - A parameterized type pattern scala.Array[T1], where T1 is a type pattern. // TODO - This type pattern matches any non-null instance of type scala.Array[U1], where U1 is a type matched by T1. - **/ - case class TypeTestTreeMaker(afterTest: Symbol, testedBinder: Symbol, expectedTp: Type, nextBinderTp: Type)(override val pos: Position, extractorArgTypeTest: Boolean = false) extends CondTreeMaker { - import TypeTestTreeMaker._ - - ctx.debuglog("TTTM" + ((prevBinder, extractorArgTypeTest, testedBinder, expectedTp, nextBinderTp))) - - val prevBinder = testedBinder - - val nextBinder = afterTest.asTerm - - def needsOuterTest(patType: Type, selType: Type, currentOwner: Symbol): Boolean = { - // See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest` - // generates an outer test based on `patType.prefix` with automatically dealises. - patType.dealias match { - case tref @ TypeRef(pre, name) => - (pre ne NoPrefix) && tref.symbol.isClass && - ExplicitOuter.needsOuterIfReferenced(tref.symbol.asClass) - case _ => - false - } - } - - override lazy val introducedRebindings = NoRebindings - - def outerTestNeeded = { - val np = expectedTp.normalizedPrefix - val ts = np.termSymbol - (ts ne NoSymbol) && needsOuterTest(expectedTp, testedBinder.info, ctx.owner) - } - - // the logic to generate the run-time test that follows from the fact that - // a `prevBinder` is expected to have type `expectedTp` - // the actual tree-generation logic is factored out, since the analyses generate Cond(ition)s rather than Trees - // TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null` - // don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false") - def renderCondition(cs: TypeTestCondStrategy): cs.Result = { - import cs._ - - // propagate expected type - def expTp(t: Tree): t.type = t // setType expectedTp todo: - - def testedWide = testedBinder.info.widen - def expectedWide = expectedTp.widen - def isAnyRef = testedWide <:< defn.AnyRefType - def isAsExpected = testedWide <:< expectedTp - def isExpectedPrimitiveType = isAsExpected && expectedTp.classSymbol.isPrimitiveValueClass - def isExpectedReferenceType = isAsExpected && (expectedTp <:< defn.AnyRefType) - def mkNullTest = nonNullTest(testedBinder) - def mkOuterTest = outerTest(testedBinder, expectedTp) - def mkTypeTest = typeTest(testedBinder, expectedWide) - - def mkEqualsTest(lhs: Tree): cs.Result = equalsTest(lhs, testedBinder) - def mkEqTest(lhs: Tree): cs.Result = eqTest(lhs, testedBinder) - def addOuterTest(res: cs.Result): cs.Result = if (outerTestNeeded) and(res, mkOuterTest) else res - - // If we conform to expected primitive type: - // it cannot be null and cannot have an outer pointer. No further checking. - // If we conform to expected reference type: - // have to test outer and non-null - // If we do not conform to expected type: - // have to test type and outer (non-null is implied by successful type test) - def mkDefault = ( - if (isExpectedPrimitiveType) tru - else addOuterTest( - if (isExpectedReferenceType) mkNullTest - else mkTypeTest - ) - ) - - // true when called to type-test the argument to an extractor - // don't do any fancy equality checking, just test the type - // TODO: verify that we don't need to special-case Array - // I think it's okay: - // - the isInstanceOf test includes a test for the element type - // - Scala's arrays are invariant (so we don't drop type tests unsoundly) - if (extractorArgTypeTest) mkDefault - else expectedTp match { - case ThisType(tref) if tref.symbol.flags is Flags.Module => - and(mkEqualsTest(ref(tref.symbol.companionModule)), mkTypeTest) // must use == to support e.g. List() == Nil - case ConstantType(Constant(null)) if isAnyRef => mkEqTest(expTp(Literal(Constant(null)))) - case ConstantType(const) => mkEqualsTest(expTp(Literal(const))) - case t: SingletonType => mkEqTest(singleton(expectedTp)) // SI-4577, SI-4897 - //case ThisType(sym) => mkEqTest(expTp(This(sym))) - case _ => mkDefault - } - } - - val cond = renderCondition(treeCondStrategy) - val res = codegen._asInstanceOf(testedBinder, nextBinderTp) - - // is this purely a type test, e.g. no outer check, no equality tests (used in switch emission) - //def isPureTypeTest = renderCondition(pureTypeTestChecker) - - def impliesBinderNonNull(binder: Symbol): Boolean = - // @odersky: scalac is able to infer in this method that nonNullImpliedByTestChecker.Result, - // dotty instead infers type projection TreeMakers.this.TypeTestTreeMaker.TypeTestCondStrategy#Result - // which in turn doesn't typecheck in this method. Can you please explain why? - // dotty deviation - renderCondition(nonNullImpliedByTestChecker(binder)).asInstanceOf[Boolean] - - override def toString = "TT" + ((expectedTp, testedBinder.name, nextBinderTp)) - } - - // need to substitute to deal with existential types -- TODO: deal with existentials better, don't substitute (see RichClass during quick.comp) - case class EqualityTestTreeMaker(prevBinder: Symbol, subpatBinder: Symbol, patTree: Tree, override val pos: Position) extends CondTreeMaker { - val nextBinderTp = patTree.tpe & prevBinder.info - val nextBinder = if (prevBinder eq subpatBinder) freshSym(pos, nextBinderTp) else subpatBinder - - // NOTE: generate `patTree == patBinder`, since the extractor must be in control of the equals method (also, patBinder may be null) - // equals need not be well-behaved, so don't intersect with pattern's (stabilized) type (unlike MaybeBoundTyped's accumType, where it's required) - val cond = codegen._equals(patTree, prevBinder) - val res = ref(prevBinder).ensureConforms(nextBinderTp) - override def toString = "ET" + ((prevBinder.name, patTree)) - } - - case class AlternativesTreeMaker(prevBinder: Symbol, var altss: List[List[TreeMaker]], pos: Position) extends TreeMaker with NoNewBinders { - // don't substitute prevBinder to nextBinder, a set of alternatives does not need to introduce a new binder, simply reuse the previous one - - override private[TreeMakers] def incorporateOuterRebinding(outerSubst: Rebindings): Unit = { - super.incorporateOuterRebinding(outerSubst) - altss = altss map (alts => propagateRebindings(alts, rebindings)) - } - - def chainBefore(next: Tree)(codegenAlt: Casegen): Tree = { - /*atPos(pos)*/{ - // one alternative may still generate multiple trees (e.g., an extractor call + equality test) - // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers - val combinedAlts = altss map (altTreeMakers => - ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(Literal(Constant(true)))))(casegen)) - ) - - val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, defn.BooleanType)(combinedAlts, Some((x: Symbol) => Literal(Constant(false)))) - codegenAlt.ifThenElseZero(findAltMatcher, next) - } - } - } - - case class GuardTreeMaker(guardTree: Tree) extends TreeMaker with NoNewBinders { - val pos = guardTree.pos - - def chainBefore(next: Tree)(casegen: Casegen): Tree = casegen.flatMapGuard(guardTree, next) - override def toString = "G(" + guardTree + ")" - } - - // combineExtractors changes the current substitution's of the tree makers in `treeMakers` - // requires propagateSubstitution(treeMakers) has been called - def combineExtractors(treeMakers: List[TreeMaker])(casegen: Casegen): Tree = { - val (testsMakers, guardAndBodyMakers) = treeMakers.span(t => !(t.isInstanceOf[NoNewBinders])) - val body = guardAndBodyMakers.foldRight(EmptyTree: Tree)((a, b) => a.chainBefore(b)(casegen)) - val rebindings = guardAndBodyMakers.last.rebindings.emitValDefs - testsMakers.foldRight(Block(rebindings, body): Tree)((a, b) => a.chainBefore(b)(casegen)) - } - // a foldLeft to accumulate the localSubstitution left-to-right - // unlike in scalace it does not drop SubstOnly tree makers, - // as there could be types having them as prefix - def propagateRebindings(treeMakers: List[TreeMaker], initial: Rebindings): List[TreeMaker] = { - var accumSubst: Rebindings = initial - treeMakers foreach { maker => - maker incorporateOuterRebinding accumSubst - accumSubst = maker.rebindings - } - treeMakers - } - - // calls propagateSubstitution on the treemakers - def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Symbol => Tree]): Tree = { - // unlike in scalac SubstOnlyTreeMakers are maintained. - val casesRebindingPropagated = casesRaw map (propagateRebindings(_, NoRebindings)) - - def matchFailGen = matchFailGenOverride orElse Some((arg: Symbol) => Throw(New(defn.MatchErrorType, List(ref(arg))))) - - ctx.debuglog("combining cases: " + (casesRebindingPropagated.map(_.mkString(" >> ")).mkString("{", "\n", "}"))) - - val (suppression, requireSwitch): (Suppression, Boolean) = - /*if (settings.XnoPatmatAnalysis)*/ (Suppression.NoSuppression, false) - /*else scrut match { - case Typed(tree, tpt) => - val suppressExhaustive = tpt.tpe hasAnnotation UncheckedClass - val supressUnreachable = tree match { - case Ident(name) if name startsWith nme.CHECK_IF_REFUTABLE_STRING => true // SI-7183 don't warn for withFilter's that turn out to be irrefutable. - case _ => false - } - val suppression = Suppression(suppressExhaustive, supressUnreachable) - // matches with two or fewer cases need not apply for switchiness (if-then-else will do) - val requireSwitch = treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0 - (suppression, requireSwitch) - case _ => - (Suppression.NoSuppression, false) - }*/ - - emitSwitch(scrut, scrutSym, casesRebindingPropagated, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{ - if (requireSwitch) ctx.warning("could not emit switch for @switch annotated match", scrut.pos) - - if (casesRebindingPropagated nonEmpty) { - // before optimizing, check casesNoSubstOnly for presence of a default case, - // since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one - // exhaustivity and reachability must be checked before optimization as well - // TODO: improve notion of trivial/irrefutable -- a trivial type test before the body still makes for a default case - // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) - // irrefutability checking should use the approximation framework also used for CSE, unreachability and exhaustivity checking - val synthCatchAll: Option[Symbol => Tree] = - if (casesRebindingPropagated.nonEmpty && { - val nonTrivLast = casesRebindingPropagated.last - nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] - }) None - else matchFailGen - - analyzeCases(scrutSym, casesRebindingPropagated, pt, suppression) - - val (cases, toHoist) = optimizeCases(scrutSym, casesRebindingPropagated, pt) - - val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases.map(x => combineExtractors(x) _), synthCatchAll) - - if (toHoist isEmpty) matchRes else Block(toHoist, matchRes) - } else { - codegen.matcher(scrut, scrutSym, pt)(Nil, matchFailGen) - } - } - } - } - - trait MatchOptimizer extends OptimizedCodegen with TreeMakers - /*with SwitchEmission // todo: toBe ported - with CommonSubconditionElimination*/ { - override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = { - // TODO: do CSE on result of doDCE(prevBinder, cases, pt) - val optCases = cases// todo: doCSE(prevBinder, cases, pt) - val toHoist = Nil/*( - for (treeMakers <- optCases) - yield treeMakers.collect{case tm: ReusedCondTreeMaker => tm.treesToHoist} - ).flatten.flatten.toList*/ - (optCases, toHoist) - } - } - - trait MatchTranslator extends TreeMakers with ScalacPatternExpanders { - - def isBackquoted(x: Ident) = x.isInstanceOf[BackquotedIdent] - - def isVarPattern(pat: Tree): Boolean = pat match { - case x: BackquotedIdent => false - case x: Ident => x.name.isVariableName - case _ => false - } - - /** A conservative approximation of which patterns do not discern anything. - * They are discarded during the translation. - */ - object WildcardPattern { - def unapply(pat: Tree): Boolean = pat match { - case Typed(_, arg) if arg.tpe.isRepeatedParam => true - case Bind(nme.WILDCARD, WildcardPattern()) => true // don't skip when binding an interesting symbol! - case t if (tpd.isWildcardArg(t)) => true - case x: Ident => isVarPattern(x) - case Alternative(ps) => ps forall unapply - case EmptyTree => true - case _ => false - } - } - - object PatternBoundToUnderscore { - def unapply(pat: Tree): Boolean = pat match { - case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol! - case Ident(nme.WILDCARD) => true - case Alternative(ps) => ps forall unapply - case Typed(PatternBoundToUnderscore(), _) => false // true // Dmitry: change in dotty. Type test will be performed and the field must be stored - case _ => false - } - } - - object SymbolBound { - def unapply(tree: Tree): Option[(Symbol, Tree)] = tree match { - case Bind(_, expr) if tree.symbol.exists => Some(tree.symbol -> expr) - case _ => None - } - } - - def newBoundTree(tree: Tree, pt: Type): BoundTree = tree match { - case SymbolBound(sym, Typed(subpat, tpe)) => BoundTree(freshSym(tree.pos, pt, prefix = "pi"), tree) - case SymbolBound(sym, expr) => BoundTree(sym, expr) - case _ => BoundTree(freshSym(tree.pos, pt, prefix = "p"), tree) - } - - final case class BoundTree(binder: Symbol, tree: Tree) { - private lazy val extractor = ExtractorCall(tree, binder) - - def pos = tree.pos - def tpe = binder.info.widenDealias - def pt = unbound match { - // case Star(tpt) => this glbWith seqType(tpt.tpe) dd todo: - case TypeBound(tpe) => tpe - case tree => tree.tpe - } - - def glbWith(other: Type) = ctx.typeComparer.glb(tpe :: other :: Nil)// .normalize - - object SymbolAndTypeBound { - def unapply(tree: Tree): Option[(Symbol, Type)] = tree match { - case SymbolBound(sym, Typed(_: UnApply, _)) => None // see comment in #189 - case SymbolBound(sym, TypeBound(tpe)) => Some(sym -> tpe) - case TypeBound(tpe) => Some(binder -> tpe) - case _ => None - } - } - - object SymbolAndValueBound { - def unapply(tree: Tree): Option[(Symbol, Tree)] = tree match { - case SymbolBound(sym, ConstantPattern(const)) => Some(sym -> const) - case _ => None - } - } - - object TypeBound { - def unapply(tree: Tree): Option[Type] = tree match { - case Typed(_, arg) if !arg.tpe.isRepeatedParam => Some(tree.typeOpt) - case _ => None - } - } - - object ConstantPattern { - def unapply(tree: Tree): Option[Tree] = tree match { - case Literal(Constant(_)) | Ident(_) | Select(_, _) | This(_) => Some(tree) - case _ => None - } - } - - private def rebindTo(pattern: Tree) = BoundTree(binder, pattern) - private def step(treeMakers: TreeMaker*)(subpatterns: BoundTree*): TranslationStep = TranslationStep(treeMakers.toList, subpatterns.toList) - - private def bindingStep(sub: Symbol, subpattern: Tree) = step(SubstOnlyTreeMaker(sub, binder))(rebindTo(subpattern)) - private def equalityTestStep(testedSymbol: Symbol, constantSymbol: Symbol, constant: Tree) - = step(EqualityTestTreeMaker(testedSymbol, constantSymbol, constant, pos))() - private def typeTestStep(sub: Symbol, subPt: Type) = step(TypeTestTreeMaker(sub, binder, subPt, sub.termRef)(pos))() - private def alternativesStep(alts: List[Tree]) = step(AlternativesTreeMaker(binder, translatedAlts(alts), alts.head.pos))() - private def translatedAlts(alts: List[Tree]) = alts map (alt => rebindTo(alt).translate()) - private def noStep() = step()() - - private def unsupportedPatternMsg = - i"unsupported pattern: ${tree.show} / $this (this is a scalac bug.)" - - // example check: List[Int] <:< ::[Int] - private def extractorStep(): TranslationStep = { - def paramType = extractor.aligner.wholeType - import extractor.treeMaker - // chain a type-testing extractor before the actual extractor call - // it tests the type, checks the outer pointer and casts to the expected type - // TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC] - // (the prefix of the argument passed to the unapply must equal the prefix of the type of the binder) - lazy val typeTest = TypeTestTreeMaker(freshSym(pos, paramType), binder, paramType, paramType)(pos, extractorArgTypeTest = true) - // check whether typetest implies binder is not null, - // even though the eventual null check will be on typeTest.nextBinder - // it'll be equal to binder casted to paramType anyway (and the type test is on binder) - def extraction: TreeMaker = treeMaker(typeTest.nextBinder, typeTest.impliesBinderNonNull(binder), pos, paramType) - - // paramType = the type expected by the unapply - // TODO: paramType may contain unbound type params (run/t2800, run/t3530) - val makers = ( - // Statically conforms to paramType - if (tpe <:< paramType) treeMaker(binder, false, pos, tpe) :: Nil - else typeTest :: extraction :: Nil - ) - step(makers: _*)(extractor.subBoundTrees: _*) - } - - // Summary of translation cases. I moved the excerpts from the specification further below so all - // the logic can be seen at once. - // - // [1] skip wildcard trees -- no point in checking them - // [2] extractor and constructor patterns - // [3] replace subpatBinder by patBinder, as if the Bind was not there. - // It must be patBinder, as subpatBinder has the wrong info: even if the bind assumes a better type, - // this is not guaranteed until we cast - // [4] typed patterns - a typed pattern never has any subtrees - // must treat Typed and Bind together -- we need to know the patBinder of the Bind pattern to get at the actual type - // [5] literal and stable id patterns - // [6] pattern alternatives - // [7] symbol-less bind patterns - this happens in certain ill-formed programs, there'll be an error later - // don't fail here though (or should we?) - def nextStep(): TranslationStep = tree match { - case _: UnApply | _: Apply | Typed(_: UnApply | _: Apply, _) => extractorStep() - case SymbolAndTypeBound(sym, tpe) => typeTestStep(sym, tpe) - case TypeBound(tpe) => typeTestStep(binder, tpe) - case SymbolBound(sym, expr) => bindingStep(sym, expr) - case WildcardPattern() => noStep() - case ConstantPattern(const) => equalityTestStep(binder, binder, const) - case Alternative(alts) => alternativesStep(alts) - case _ => ctx.error(unsupportedPatternMsg, pos) ; noStep() - } - def translate(): List[TreeMaker] = nextStep() merge (_.translate()) - - private def concreteType = tpe.bounds.hi - private def unbound = unbind(tree) - private def tpe_s = if (pt <:< concreteType) "" + pt else s"$pt (binder: $tpe)" - private def at_s = unbound match { - case WildcardPattern() => "" - case pat => s" @ $pat" - } - override def toString = s"${binder.name}: $tpe_s$at_s" - } - - // a list of TreeMakers that encode `patTree`, and a list of arguments for recursive invocations of `translatePattern` to encode its subpatterns - final case class TranslationStep(makers: List[TreeMaker], subpatterns: List[BoundTree]) { - def merge(f: BoundTree => List[TreeMaker]): List[TreeMaker] = makers ::: (subpatterns flatMap f) - override def toString = if (subpatterns.isEmpty) "" else subpatterns.mkString("(", ", ", ")") - } - - def isSyntheticDefaultCase(cdef: CaseDef) = cdef match { - case CaseDef(Bind(nme.DEFAULT_CASE, _), EmptyTree, _) => true - case _ => false - } - - /** Implement a pattern match by turning its cases (including the implicit failure case) - * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. - * - * For `scrutinee match { case1 ... caseN }`, the resulting tree has the shape - * `runOrElse(scrutinee)(x => translateCase1(x).orElse(translateCase2(x)).....orElse(zero))` - * - * NOTE: the resulting tree is not type checked, nor are nested pattern matches transformed - * thus, you must typecheck the result (and that will in turn translate nested matches) - * this could probably be optimized... (but note that the matchStrategy must be solved for each nested patternmatch) - */ - def translateMatch(match_ : Match): Tree = { - val Match(sel, cases) = match_ - - val selectorTp = sel.tpe.widen.deAnonymize/*withoutAnnotations*/ - - val selectorSym = freshSym(sel.pos, selectorTp, "selector") - - val (nonSyntheticCases, defaultOverride) = cases match { - case init :+ last if isSyntheticDefaultCase(last) => (init, Some(((scrut: Symbol) => last.body))) - case _ => (cases, None) - } - - - // checkMatchVariablePatterns(nonSyntheticCases) // only used for warnings - - // we don't transform after uncurry - // (that would require more sophistication when generating trees, - // and the only place that emits Matches after typers is for exception handling anyway) - /*if (phase.id >= currentRun.uncurryPhase.id) - devWarning(s"running translateMatch past uncurry (at $phase) on $selector match $cases")*/ - - ctx.debuglog("translating " + cases.mkString("{", "\n", "}")) - - //val start = if (Statistics.canEnable) Statistics.startTimer(patmatNanos) else null - - // when one of the internal cps-type-state annotations is present, strip all CPS annotations - ///val origPt = removeCPSFromPt(match_.tpe) - // relevant test cases: pos/existentials-harmful.scala, pos/gadt-gilles.scala, pos/t2683.scala, pos/virtpatmat_exist4.scala - // pt is the skolemized version - val pt = match_.tpe.widen //repeatedToSeq(origPt) - - // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner)) - selectorSym.setFlag(Flags.SyntheticCase) - - // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental - val combined = combineCases(sel, selectorSym, nonSyntheticCases map translateCase(selectorSym, pt), pt, ctx.owner, defaultOverride) - - // if (Statistics.canEnable) Statistics.stopTimer(patmatNanos, start) - Block(List(ValDef(selectorSym, sel)), combined) - } - - /** The translation of `pat if guard => body` has two aspects: - * 1) the substitution due to the variables bound by patterns - * 2) the combination of the extractor calls using `flatMap`. - * - * 2) is easy -- it looks like: `translatePattern_1.flatMap(translatePattern_2....flatMap(translatePattern_N.flatMap(translateGuard.flatMap((x_i) => success(Xbody(x_i)))))...)` - * this must be right-leaning tree, as can be seen intuitively by considering the scope of bound variables: - * variables bound by pat_1 must be visible from the function inside the left-most flatMap right up to Xbody all the way on the right - * 1) is tricky because translatePattern_i determines the shape of translatePattern_i + 1: - * zoom in on `translatePattern_1.flatMap(translatePattern_2)` for example -- it actually looks more like: - * `translatePattern_1(x_scrut).flatMap((x_1) => {y_i -> x_1._i}translatePattern_2)` - * - * `x_1` references the result (inside the monad) of the extractor corresponding to `pat_1`, - * this result holds the values for the constructor arguments, which translatePattern_1 has extracted - * from the object pointed to by `x_scrut`. The `y_i` are the symbols bound by `pat_1` (in order) - * in the scope of the remainder of the pattern, and they must thus be replaced by: - * - (for 1-ary unapply) x_1 - * - (for n-ary unapply, n > 1) selection of the i'th tuple component of `x_1` - * - (for unapplySeq) x_1.apply(i) - * - * in the treemakers, - * - * Thus, the result type of `translatePattern_i`'s extractor must conform to `M[(T_1,..., T_n)]`. - * - * Operationally, phase 1) is a foldLeft, since we must consider the depth-first-flattening of - * the transformed patterns from left to right. For every pattern ast node, it produces a transformed ast and - * a function that will take care of binding and substitution of the next ast (to the right). - * - */ - def translateCase(scrutSym: Symbol, pt: Type)(caseDef: CaseDef): List[TreeMaker] = { - val CaseDef(pattern, guard, body) = caseDef - translatePattern(BoundTree(scrutSym, pattern)) ++ translateGuard(guard) :+ translateBody(body, pt) - } - - def translatePattern(bound: BoundTree): List[TreeMaker] = bound.translate() - - def translateGuard(guard: Tree): List[TreeMaker] = - if (guard == EmptyTree) Nil - else List(GuardTreeMaker(guard)) - - // TODO: 1) if we want to support a generalisation of Kotlin's patmat continue, must not hard-wire lifting into the monad (which is now done by codegen.one), - // so that user can generate failure when needed -- use implicit conversion to lift into monad on-demand? - // to enable this, probably need to move away from Option to a monad specific to pattern-match, - // so that we can return Option's from a match without ambiguity whether this indicates failure in the monad, or just some result in the monad - // 2) body.tpe is the type of the body after applying the substitution that represents the solution of GADT type inference - // need the explicit cast in case our substitutions in the body change the type to something that doesn't take GADT typing into account - def translateBody(body: Tree, matchPt: Type): TreeMaker = - BodyTreeMaker(body, matchPt) - - // Some notes from the specification - - /*A constructor pattern is of the form c(p1, ..., pn) where n ≥ 0. - It consists of a stable identifier c, followed by element patterns p1, ..., pn. - The constructor c is a simple or qualified name which denotes a case class (§5.3.2). - - If the case class is monomorphic, then it must conform to the expected type of the pattern, - and the formal parameter types of x’s primary constructor (§5.3) are taken as the expected - types of the element patterns p1, ..., pn. - - If the case class is polymorphic, then its type parameters are instantiated so that the - instantiation of c conforms to the expected type of the pattern. - The instantiated formal parameter types of c’s primary constructor are then taken as the - expected types of the component patterns p1, ..., pn. - - The pattern matches all objects created from constructor invocations c(v1, ..., vn) - where each element pattern pi matches the corresponding value vi . - A special case arises when c’s formal parameter types end in a repeated parameter. - This is further discussed in (§8.1.9). - **/ - - /* A typed pattern x : T consists of a pattern variable x and a type pattern T. - The type of x is the type pattern T, where each type variable and wildcard is replaced by a fresh, unknown type. - This pattern matches any value matched by the type pattern T (§8.2); it binds the variable name to that value. - */ - - /* A pattern binder x@p consists of a pattern variable x and a pattern p. - The type of the variable x is the static type T of the pattern p. - This pattern matches any value v matched by the pattern p, - provided the run-time type of v is also an instance of T, <-- TODO! https://issues.scala-lang.org/browse/SI-1503 - and it binds the variable name to that value. - */ - - /* 8.1.4 Literal Patterns - A literal pattern L matches any value that is equal (in terms of ==) to the literal L. - The type of L must conform to the expected type of the pattern. - - 8.1.5 Stable Identifier Patterns (a stable identifier r (see §3.1)) - The pattern matches any value v such that r == v (§12.1). - The type of r must conform to the expected type of the pattern. - */ - - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // helper methods: they analyze types and trees in isolation, but they are not (directly) concerned with the structure of the overall translation - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - object ExtractorCall { - // TODO: check unargs == args - def apply(tree: Tree, binder: Symbol): ExtractorCall = { - tree match { - case UnApply(unfun, implicits, args) => - val castedBinder = ref(binder).ensureConforms(tree.tpe) - val synth = if (implicits.isEmpty) unfun.appliedTo(castedBinder) else unfun.appliedTo(castedBinder).appliedToArgs(implicits) - new ExtractorCallRegular(alignPatterns(tree, synth.tpe), synth, args, synth.tpe) // extractor - case Typed(unapply@ UnApply(unfun, implicits, args), tpt) => - val castedBinder = ref(binder).ensureConforms(unapply.tpe) - val synth = /*Typed(*/ if (implicits.isEmpty) unfun.appliedTo(castedBinder) else unfun.appliedTo(castedBinder).appliedToArgs(implicits) //, tpt) - new ExtractorCallRegular(alignPatterns(tree, synth.tpe), synth, args, synth.tpe) // extractor - case Apply(fun, args) => new ExtractorCallProd(alignPatterns(tree, tree.tpe), fun, args, fun.tpe) // case class - } - } - } - - abstract class ExtractorCall(val aligner: PatternAligned) { - - import aligner._ - - def args: List[Tree] - - // don't go looking for selectors if we only expect one pattern - def rawSubPatTypes = aligner.extractedTypes - - def typeArgOfBaseTypeOr(tp: Type, baseClass: Symbol)(or: => Type): Type = (tp.baseTypeWithArgs(baseClass)).argInfos match { - case x :: Nil => x - case _ => or - } - - def resultInMonad = if (aligner.isBool) defn.UnitType else { - val getTp = extractorMemberType(resultType, nme.get) - if ((extractorMemberType(resultType, nme.isDefined) isRef defn.BooleanClass) && getTp.exists) - getTp - else resultType - } - def resultType: Type - - /** Create the TreeMaker that embodies this extractor call - * - * `binder` has been casted to `paramType` if necessary - * `binderKnownNonNull` indicates whether the cast implies `binder` cannot be null - * when `binderKnownNonNull` is `true`, `ProductExtractorTreeMaker` does not do a (redundant) null check on binder - */ - def treeMaker(binder: Symbol, binderKnownNonNull: Boolean, pos: Position, binderTypeTested: Type): TreeMaker - - // `subPatBinders` are the variables bound by this pattern in the following patterns - // subPatBinders are replaced by references to the relevant part of the extractor's result (tuple component, seq element, the result as-is) - // must set infos to `subPatTypes`, which are provided by extractor's result, - // as b.info may be based on a Typed type ascription, which has not been taken into account yet by the translation - // (it will later result in a type test when `tp` is not a subtype of `b.info`) - // TODO: can we simplify this, together with the Bound case? - def subPatBinders = subBoundTrees map (_.binder) - lazy val subBoundTrees = (args, subPatTypes).zipped map newBoundTree - - // never store these in local variables (for PreserveSubPatBinders) - lazy val ignoredSubPatBinders: Set[Symbol] = subPatBinders zip args collect { case (b, PatternBoundToUnderscore()) => b } toSet - - // do repeated-parameter expansion to match up with the expected number of arguments (in casu, subpatterns) - private def nonStarSubPatTypes = aligner.typedNonStarPatterns map (_.tpe) - - def subPatTypes: List[Type] = typedPatterns map (_.tpe) - - // there are `prodArity` non-seq elements in the tuple. - protected def firstIndexingBinder = prodArity - protected def expectedLength = elementArity - protected def lastIndexingBinder = totalArity - starArity - 1 - - private def productElemsToN(binder: Symbol, n: Int): List[Tree] = 1 to n map tupleSel(binder) toList - private def genTake(binder: Symbol, n: Int): List[Tree] = (0 until n).toList map (codegen index seqTree(binder)) - private def genDrop(binder: Symbol, n: Int): List[Tree] = codegen.drop(seqTree(binder))(expectedLength) :: Nil - - // codegen.drop(seqTree(binder))(nbIndexingIndices)))).toList - protected def seqTree(binder: Symbol) = tupleSel(binder)(firstIndexingBinder + 1) - protected def tupleSel(binder: Symbol)(i: Int): Tree = { - val accessors = - if (defn.isProductSubType(binder.info)) - productSelectors(binder.info) - else binder.caseAccessors - val res = - if (accessors.isDefinedAt(i - 1)) ref(binder).select(accessors(i - 1).name) - else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN - val rsym = res.symbol // just for debugging - res - } - - // the trees that select the subpatterns on the extractor's result, - // referenced by `binder` - protected def subPatRefsSeq(binder: Symbol): List[Tree] = { - def lastTrees: List[Tree] = ( - if (!aligner.isStar) Nil - else if (expectedLength == 0) seqTree(binder) :: Nil - else genDrop(binder, expectedLength) - ) - // this error-condition has already been checked by checkStarPatOK: - // if (isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if (lastIsStar) 1 else 0) == totalArity, "(resultInMonad, ts, subPatTypes, subPats)= " +(resultInMonad, ts, subPatTypes, subPats)) - - // [1] there are `firstIndexingBinder` non-seq tuple elements preceding the Seq - // [2] then we have to index the binder that represents the sequence for the remaining subpatterns, except for... - // [3] the last one -- if the last subpattern is a sequence wildcard: - // drop the prefix (indexed by the refs on the preceding line), return the remainder - ( productElemsToN(binder, firstIndexingBinder) - ++ genTake(binder, expectedLength) - ++ lastTrees - ).toList - } - - // the trees that select the subpatterns on the extractor's result, referenced by `binder` - // require (nbSubPats > 0 && (!lastIsStar || isSeq)) - protected def subPatRefs(binder: Symbol): List[Tree] = { - val refs = if (totalArity > 0 && isSeq) subPatRefsSeq(binder) - else if (binder.info.member(nme._1).exists && !isSeq) productElemsToN(binder, totalArity) - else ref(binder) :: Nil - refs - } - - val mathSignymSymbol = defn.ScalaMathPackageVal.requiredMethod("signum".toTermName, List(defn.IntType)) - val mathSignum = ref(defn.ScalaMathPackageVal).select(mathSignymSymbol) - - - private def compareInts(t1: Tree, t2: Tree) = - mathSignum.appliedTo(t1.select(defn.Int_-).appliedTo(t2)) - //gen.mkMethodCall(termMember(ScalaPackage, "math"), TermName("signum"), Nil, (t1 INT_- t2) :: Nil) - - protected def lengthGuard(binder: Symbol): Option[Tree] = - // no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied - checkedLength map { expectedLength => - // `binder.lengthCompare(expectedLength)` - // ...if binder has a lengthCompare method, otherwise - // `scala.math.signum(binder.length - expectedLength)` - def checkExpectedLength: Tree = sequenceType.member(nme.lengthCompare) match { - case NoDenotation => compareInts(Select(seqTree(binder), nme.length), Literal(Constant(expectedLength))) - case x:SingleDenotation => (seqTree(binder).select(x.symbol)).appliedTo(Literal(Constant(expectedLength))) - case _ => - ctx.error("TODO: multiple lengthCompare") - EmptyTree - } - - // the comparison to perform - // when the last subpattern is a wildcard-star the expectedLength is but a lower bound - // (otherwise equality is required) - def compareOp: (Tree, Tree) => Tree = - if (aligner.isStar) _.select(defn.Int_>=).appliedTo(_) - else _.select(defn.Int_==).appliedTo(_) - - // `if (binder != null && $checkExpectedLength [== | >=] 0) then else zero` - (seqTree(binder).select(defn.Any_!=).appliedTo(Literal(Constant(null)))).select(defn.Boolean_&&).appliedTo(compareOp(checkExpectedLength, Literal(Constant(0)))) - } - - def checkedLength: Option[Int] = - // no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied - if (!isSeq || expectedLength < starArity) None - else Some(expectedLength) - } - - // TODO: to be called when there's a def unapplyProd(x: T): U - // U must have N members _1,..., _N -- the _i are type checked, call their type Ti, - // for now only used for case classes -- pretending there's an unapplyProd that's the identity (and don't call it) - class ExtractorCallProd(aligner: PatternAligned, val fun: Tree, val args: List[Tree], val resultType: Type) extends ExtractorCall(aligner) { - /** Create the TreeMaker that embodies this extractor call - * - * `binder` has been casted to `paramType` if necessary - * `binderKnownNonNull` indicates whether the cast implies `binder` cannot be null - * when `binderKnownNonNull` is `true`, `ProductExtractorTreeMaker` does not do a (redundant) null check on binder - */ - def treeMaker(binder: Symbol, binderKnownNonNull: Boolean, pos: Position, binderTypeTested: Type): TreeMaker = { - val paramAccessors = binder.caseAccessors - // binders corresponding to mutable fields should be stored (SI-5158, SI-6070) - // make an exception for classes under the scala package as they should be well-behaved, - // to optimize matching on List - val mutableBinders = ( - if (//!binder.info.typeSymbol.hasTransOwner(ScalaPackageClass) // TODO: DDD ??? - // && - (paramAccessors exists (_.hasAltWith(x => x.symbol is Flags.Mutable)))) - subPatBinders.zipWithIndex.collect{ case (binder, idx) if paramAccessors(idx).hasAltWith(x => x.symbol is Flags.Mutable) => binder } - else Nil - ) - - // checks binder ne null before chaining to the next extractor - ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder), mutableBinders, binderKnownNonNull, ignoredSubPatBinders) - } - } - - class ExtractorCallRegular(aligner: PatternAligned, extractorCallIncludingDummy: Tree, val args: List[Tree], val resultType: Type) extends ExtractorCall(aligner) { - - /** Create the TreeMaker that embodies this extractor call - * - * `binder` has been casted to `paramType` if necessary - * `binderKnownNonNull` is not used in this subclass - * - * TODO: implement review feedback by @retronym: - * Passing the pair of values around suggests: - * case class Binder(sym: Symbol, knownNotNull: Boolean). - * Perhaps it hasn't reached critical mass, but it would already clean things up a touch. - */ - def treeMaker(patBinderOrCasted: Symbol, binderKnownNonNull: Boolean, pos: Position, binderTypeTested: Type): TreeMaker = { - // the extractor call (applied to the binder bound by the flatMap corresponding - // to the previous (i.e., enclosing/outer) pattern) - val extractorApply = extractorCallIncludingDummy// spliceApply(patBinderOrCasted) - // can't simplify this when subPatBinders.isEmpty, since UnitTpe is definitely - // wrong when isSeq, and resultInMonad should always be correct since it comes - // directly from the extractor's result type - val binder = freshSym(pos, resultInMonad) - val spb = subPatBinders - ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)( - spb, - subPatRefs(binder, spb, resultType), - aligner.isBool, - checkedLength, - patBinderOrCasted, - ignoredSubPatBinders - ) - } - - override protected def seqTree(binder: Symbol): Tree = - if (firstIndexingBinder == 0) ref(binder) - else super.seqTree(binder) - - // the trees that select the subpatterns on the extractor's result, referenced by `binder` - // require (totalArity > 0 && (!lastIsStar || isSeq)) - protected def subPatRefs(binder: Symbol, subpatBinders: List[Symbol], binderTypeTested: Type): List[Tree] = { - if (aligner.isSingle && aligner.extractor.prodArity == 1 && defn.isTupleType(binder.info)) { - // special case for extractor - // comparing with scalac additional assertions added - val subpw = subpatBinders.head.info.widen - val binderw = binder.info.widen - val go = subpatBinders.head.info <:< binder.info - val go1 = binder.info <:< subpatBinders.head.info - //val spr = subPatRefs(binder) - assert(go && go1) - ref(binder) :: Nil - } else { - lazy val getTp = extractorMemberType(binderTypeTested, nme.get) - if ((aligner.isSingle && aligner.extractor.prodArity == 1) && ((extractorMemberType(binderTypeTested, nme.isDefined) isRef defn.BooleanClass) && getTp.exists)) - List(ref(binder)) - else - subPatRefs(binder) - } - } - - /*protected def spliceApply(binder: Symbol): Tree = { - object splice extends TreeMap { - def binderRef(pos: Position): Tree = - ref(binder) //setPos pos - - override def transform(t: tpd.Tree)(implicit ctx: Context): tpd.Tree = t match { - // duplicated with the extractor Unapplied - case Apply(x, List(i @ Ident(nme.SELECTOR_DUMMY))) => - cpy.Apply(t, x, binderRef(i.pos) :: Nil) - // SI-7868 Account for numeric widening, e.g. <unappplySelector>.toInt - case Apply(x, List(i @ (sel @ Select(Ident(nme.SELECTOR_DUMMY), name)))) => - cpy.Apply(t, x, cpy.Select(sel, binderRef(i.pos), name) :: Nil) - case _ => - super.transform(t) - } - } - splice transform extractorCallIncludingDummy - }*/ - - override def rawSubPatTypes = aligner.extractor.varargsTypes - } - } - - /** An extractor returns: F1, F2, ..., Fi, opt[Seq[E] or E*] - * A case matches: P1, P2, ..., Pj, opt[Seq[E]] - * Put together: P1/F1, P2/F2, ... Pi/Fi, Pi+1/E, Pi+2/E, ... Pj/E, opt[Seq[E]] - * - * Here Pm/Fi is the last pattern to match the fixed arity section. - * - * prodArity: the value of i, i.e. the number of non-sequence types in the extractor - * nonStarArity: the value of j, i.e. the number of non-star patterns in the case definition - * elementArity: j - i, i.e. the number of non-star patterns which must match sequence elements - * starArity: 1 or 0 based on whether there is a star (sequence-absorbing) pattern - * totalArity: nonStarArity + starArity, i.e. the number of patterns in the case definition - * - * Note that prodArity is a function only of the extractor, and - * nonStar/star/totalArity are all functions of the patterns. The key - * value for aligning and typing the patterns is elementArity, as it - * is derived from both sets of information. - */ - trait PatternExpander[Pattern, Type] { - /** You'll note we're not inside the cake. "Pattern" and "Type" are - * arbitrary types here, and NoPattern and NoType arbitrary values. - */ - def NoPattern: Pattern - def NoType: Type - - /** It's not optimal that we're carrying both sequence and repeated - * type here, but the implementation requires more unraveling before - * it can be avoided. - * - * sequenceType is Seq[T], elementType is T, repeatedType is T*. - */ - sealed case class Repeated(sequenceType: Type, elementType: Type, repeatedType: Type) { - def exists = elementType != NoType - - def elementList = if (exists) elementType :: Nil else Nil - def sequenceList = if (exists) sequenceType :: Nil else Nil - def repeatedList = if (exists) repeatedType :: Nil else Nil - - override def toString = s"${elementType}*" - } - object NoRepeated extends Repeated(NoType, NoType, NoType) { - override def toString = "<none>" - } - - final case class Patterns(fixed: List[Pattern], star: Pattern) { - def hasStar = star != NoPattern - def starArity = if (hasStar) 1 else 0 - def nonStarArity = fixed.length - def totalArity = nonStarArity + starArity - def starPatterns = if (hasStar) star :: Nil else Nil - def all = fixed ::: starPatterns - - override def toString = all mkString ", " - } - - /** An 'extractor' can be a case class or an unapply or unapplySeq method. - * Decoding what it is that they extract takes place before we arrive here, - * so that this class can concentrate only on the relationship between - * patterns and types. - * - * In a case class, the class is the unextracted type and the fixed and - * repeated types are derived from its constructor parameters. - * - * In an unapply, this is reversed: the parameter to the unapply is the - * unextracted type, and the other types are derived based on the return - * type of the unapply method. - * - * In other words, this case class and unapply are encoded the same: - * - * case class Foo(x: Int, y: Int, zs: Char*) - * def unapplySeq(x: Foo): Option[(Int, Int, Seq[Char])] - * - * Both are Extractor(Foo, Int :: Int :: Nil, Repeated(Seq[Char], Char, Char*)) - * - * @param whole The type in its unextracted form - * @param fixed The non-sequence types which are extracted - * @param repeated The sequence type which is extracted - */ - final case class Extractor(whole: Type, fixed: List[Type], repeated: Repeated) { - require(whole != NoType, s"expandTypes($whole, $fixed, $repeated)") - - def prodArity = fixed.length - def hasSeq = repeated.exists - def elementType = repeated.elementType - def sequenceType = repeated.sequenceType - def allTypes = fixed ::: repeated.sequenceList - def varargsTypes = fixed ::: repeated.repeatedList - def isErroneous = allTypes contains NoType - - private def typeStrings = fixed.map("" + _) ::: ( if (hasSeq) List("" + repeated) else Nil ) - - def offeringString = if (isErroneous) "<error>" else typeStrings match { - case Nil => "Boolean" - case tp :: Nil => tp - case tps => tps.mkString("(", ", ", ")") - } - override def toString = "%s => %s".format(whole, offeringString) - } - - final case class TypedPat(pat: Pattern, tpe: Type) { - override def toString = s"$pat: $tpe" - } - - /** If elementArity is... - * 0: A perfect match between extractor and the fixed patterns. - * If there is a star pattern it will match any sequence. - * > 0: There are more patterns than products. There will have to be a - * sequence which can populate at least <elementArity> patterns. - * < 0: There are more products than patterns: compile time error. - */ - final case class Aligned(patterns: Patterns, extractor: Extractor) { - def elementArity = patterns.nonStarArity - prodArity - def prodArity = extractor.prodArity - def starArity = patterns.starArity - def totalArity = patterns.totalArity - - def wholeType = extractor.whole - def sequenceType = extractor.sequenceType - def productTypes = extractor.fixed - def extractedTypes = extractor.allTypes - def typedNonStarPatterns = products ::: elements - def typedPatterns = typedNonStarPatterns ::: stars - - def isBool = !isSeq && prodArity == 0 - def isSingle = !isSeq && totalArity == 1 - def isStar = patterns.hasStar - def isSeq = extractor.hasSeq - - private def typedAsElement(pat: Pattern) = TypedPat(pat, extractor.elementType) - private def typedAsSequence(pat: Pattern) = TypedPat(pat, extractor.sequenceType) - private def productPats = patterns.fixed take prodArity - private def elementPats = patterns.fixed drop prodArity - private def products = (productPats, productTypes).zipped map TypedPat - private def elements = elementPats map typedAsElement - private def stars = patterns.starPatterns map typedAsSequence - - override def toString = s""" - |Aligned { - | patterns $patterns - | extractor $extractor - | arities $prodArity/$elementArity/$starArity // product/element/star - | typed ${typedPatterns mkString ", "} - |}""".stripMargin.trim - } - } - - /** This is scalac-specific logic layered on top of the scalac-agnostic - * "matching products to patterns" logic defined in PatternExpander. - */ - trait ScalacPatternExpanders { - - type PatternAligned = ScalacPatternExpander#Aligned - - implicit class AlignedOps(val aligned: PatternAligned) { - import aligned._ - def expectedTypes = typedPatterns map (_.tpe) - def unexpandedFormals = extractor.varargsTypes - } - - trait ScalacPatternExpander extends PatternExpander[Tree, Type] { - def NoPattern = EmptyTree - def NoType = core.Types.NoType - - def newPatterns(patterns: List[Tree]): Patterns = patterns match { - case init :+ last if tpd.isWildcardStarArg(last) => Patterns(init, last) - case _ => Patterns(patterns, NoPattern) - } - def typeOfMemberNamedHead(tpe: Type): Type = tpe.select(nme.head) - def typeOfMemberNamedApply(tpe: Type): Type = tpe.select(nme.apply) - - def elementTypeOf(tpe: Type) = { - val seq = tpe //repeatedToSeq(tpe) - - ( typeOfMemberNamedHead(seq) - orElse typeOfMemberNamedApply(seq) - orElse seq.elemType - ) - } - def newExtractor(whole: Type, fixed: List[Type], repeated: Repeated): Extractor = { - ctx.log(s"newExtractor($whole, $fixed, $repeated") - Extractor(whole, fixed, repeated) - } - - // Turn Seq[A] into Repeated(Seq[A], A, A*) - def repeatedFromSeq(seqType: Type): Repeated = { - val elem = elementTypeOf(seqType) - val repeated = /*scalaRepeatedType(*/elem//) - - Repeated(seqType, elem, repeated) - } - // Turn A* into Repeated(Seq[A], A, A*) - def repeatedFromVarargs(repeated: Type): Repeated = - //Repeated(repeatedToSeq(repeated), repeatedToSingle(repeated), repeated) - Repeated(repeated, repeated.elemType, repeated) - - /** In this case we are basing the pattern expansion on a case class constructor. - * The argument is the MethodType carried by the primary constructor. - */ - def applyMethodTypes(method: Type): Extractor = { - val whole = method.finalResultType - - method.paramTypess.head match { - case init :+ last if last.isRepeatedParam => newExtractor(whole, init, repeatedFromVarargs(last)) - case tps => newExtractor(whole, tps, NoRepeated) - } - } - - def hasSelectors(tpe: Type) = tpe.member(nme._1).exists && tpe.member(nme._2).exists // dd todo: ??? - - - /** In this case, expansion is based on an unapply or unapplySeq method. - * Unfortunately the MethodType does not carry the information of whether - * it was unapplySeq, so we have to funnel that information in separately. - */ - def unapplyMethodTypes(tree: Tree, fun: Tree, args: List[Tree], resultType: Type, isSeq: Boolean): Extractor = { - _id = _id + 1 - - val whole = tree.tpe // see scaladoc for Trees.Unapply - // fun.tpe.widen.paramTypess.headOption.flatMap(_.headOption).getOrElse(NoType)//firstParamType(method) - val resultOfGet = extractorMemberType(resultType, nme.get) - - val expanded: List[Type] = /*( - if (result =:= defn.BooleanType) Nil - else if (defn.isProductSubType(result)) productSelectorTypes(result) - else if (result.classSymbol is Flags.CaseClass) result.decls.filter(x => x.is(Flags.CaseAccessor) && x.is(Flags.Method)).map(_.info).toList - else result.select(nme.get) :: Nil - )*/ - if ((extractorMemberType(resultType, nme.isDefined) isRef defn.BooleanClass) && resultOfGet.exists) - getUnapplySelectors(resultOfGet, args) - else if (defn.isProductSubType(resultType)) productSelectorTypes(resultType) - else if (resultType isRef defn.BooleanClass) Nil - else { - ctx.error(i"invalid return type in Unapply node: $resultType") - Nil - } - - expanded match { - case init :+ last if isSeq => newExtractor(whole, init, repeatedFromSeq(last)) - case tps => newExtractor(whole, tps, NoRepeated) - } - } - } - - object alignPatterns extends ScalacPatternExpander { - /** Converts a T => (A, B, C) extractor to a T => ((A, B, CC)) extractor. - */ - def tupleExtractor(extractor: Extractor): Extractor = - extractor.copy(fixed = defn.tupleType(extractor.fixed) :: Nil) - - private def validateAligned(tree: Tree, aligned: Aligned): Aligned = { - import aligned._ - - def owner = tree.symbol.owner - def offering = extractor.offeringString - def symString = tree.symbol.showLocated - def offerString = if (extractor.isErroneous) "" else s" offering $offering" - def arityExpected = (if (extractor.hasSeq) "at least " else "") + prodArity - - def err(msg: String) = ctx.error(msg, tree.pos) - def warn(msg: String) = ctx.warning(msg, tree.pos) - def arityError(what: String) = err(s"${_id} $what patterns for $owner$offerString: expected $arityExpected, found $totalArity") - - if (isStar && !isSeq) - err("Star pattern must correspond with varargs or unapplySeq") - else if (elementArity < 0) - arityError("not enough") - else if (elementArity > 0 && !extractor.hasSeq) - arityError("too many") - - aligned - } - - object Applied { - // Duplicated with `spliceApply` - def unapply(tree: Tree): Option[Tree] = tree match { - // SI-7868 Admit Select() to account for numeric widening, e.g. <unappplySelector>.toInt - /*case Apply(fun, (Ident(nme.SELECTOR_DUMMY)| Select(Ident(nme.SELECTOR_DUMMY), _)) :: Nil) - => Some(fun)*/ - case Apply(fun, _) => unapply(fun) - case _ => None - } - } - - def apply(tree: Tree, sel: Tree, args: List[Tree], resultType: Type): Aligned = { - val fn = sel match { - case Applied(fn) => fn - case _ => sel - } - val patterns = newPatterns(args) - val isSeq = sel.symbol.name == nme.unapplySeq - val isUnapply = sel.symbol.name == nme.unapply - val extractor = sel.symbol.name match { - case nme.unapply => unapplyMethodTypes(tree, /*fn*/sel, args, resultType, isSeq = false) - case nme.unapplySeq => unapplyMethodTypes(tree, /*fn*/sel, args, resultType, isSeq = true) - case _ => applyMethodTypes(/*fn*/sel.tpe) - } - - /** Rather than let the error that is SI-6675 pollute the entire matching - * process, we will tuple the extractor before creation Aligned so that - * it contains known good values. - */ - def prodArity = extractor.prodArity - def acceptMessage = if (extractor.isErroneous) "" else s" to hold ${extractor.offeringString}" - val requiresTupling = isUnapply && patterns.totalArity == 1 && prodArity > 1 - - //if (requiresTupling && effectivePatternArity(args) == 1) - // currentUnit.deprecationWarning(sel.pos, s"${sel.symbol.owner} expects $prodArity patterns$acceptMessage but crushing into $prodArity-tuple to fit single pattern (SI-6675)") - - val normalizedExtractor = - if (requiresTupling) - tupleExtractor(extractor) - else extractor - validateAligned(fn, Aligned(patterns, normalizedExtractor)) - } - - def apply(tree: Tree, resultType: Type): Aligned = tree match { - case Typed(tree, _) => apply(tree, resultType) - case Apply(fn, args) => apply(tree, fn, args, resultType) - case UnApply(fn, implicits, args) => apply(tree, fn, args, resultType) - } - } - } - } -} diff --git a/src/dotty/tools/dotc/transform/Pickler.scala b/src/dotty/tools/dotc/transform/Pickler.scala deleted file mode 100644 index 61c3ca5de..000000000 --- a/src/dotty/tools/dotc/transform/Pickler.scala +++ /dev/null @@ -1,108 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Contexts.Context -import Decorators._ -import tasty._ -import config.Printers.{noPrinter, pickling} -import java.io.PrintStream -import Periods._ -import Phases._ -import Symbols._ -import Flags.Module -import collection.mutable - -/** This phase pickles trees */ -class Pickler extends Phase { - import ast.tpd._ - - override def phaseName: String = "pickler" - - private def output(name: String, msg: String) = { - val s = new PrintStream(name) - s.print(msg) - s.close - } - - // Maps that keep a record if -Ytest-pickler is set. - private val beforePickling = new mutable.HashMap[ClassSymbol, String] - private val picklers = new mutable.HashMap[ClassSymbol, TastyPickler] - - /** Drop any elements of this list that are linked module classes of other elements in the list */ - private def dropCompanionModuleClasses(clss: List[ClassSymbol])(implicit ctx: Context): List[ClassSymbol] = { - val companionModuleClasses = - clss.filterNot(_ is Module).map(_.linkedClass).filterNot(_.isAbsent) - clss.filterNot(companionModuleClasses.contains) - } - - override def run(implicit ctx: Context): Unit = { - val unit = ctx.compilationUnit - pickling.println(i"unpickling in run ${ctx.runId}") - - for { cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree)) - tree <- sliceTopLevel(unit.tpdTree, cls) } { - val pickler = new TastyPickler() - if (ctx.settings.YtestPickler.value) { - beforePickling(cls) = tree.show - picklers(cls) = pickler - } - val treePkl = pickler.treePkl - treePkl.pickle(tree :: Nil) - treePkl.compactify() - pickler.addrOfTree = treePkl.buf.addrOfTree - pickler.addrOfSym = treePkl.addrOfSym - if (tree.pos.exists) - new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil) - - // other pickle sections go here. - val pickled = pickler.assembleParts() - unit.pickled += (cls -> pickled) - - def rawBytes = // not needed right now, but useful to print raw format. - pickled.iterator.grouped(10).toList.zipWithIndex.map { - case (row, i) => s"${i}0: ${row.mkString(" ")}" - } - // println(i"rawBytes = \n$rawBytes%\n%") // DEBUG - if (pickling ne noPrinter) { - println(i"**** pickled info of $cls") - new TastyPrinter(pickler.assembleParts()).printContents() - } - } - } - - override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { - val result = super.runOn(units) - if (ctx.settings.YtestPickler.value) - testUnpickler( - ctx.fresh - .setPeriod(Period(ctx.runId + 1, FirstPhaseId)) - .addMode(Mode.ReadPositions)) - result - } - - private def testUnpickler(implicit ctx: Context): Unit = { - pickling.println(i"testing unpickler at run ${ctx.runId}") - ctx.initialize() - val unpicklers = - for ((cls, pickler) <- picklers) yield { - val unpickler = new DottyUnpickler(pickler.assembleParts()) - unpickler.enter(roots = Set()) - cls -> unpickler - } - pickling.println("************* entered toplevel ***********") - for ((cls, unpickler) <- unpicklers) { - val unpickled = unpickler.body - testSame(i"$unpickled%\n%", beforePickling(cls), cls) - } - } - - private def testSame(unpickled: String, previous: String, cls: ClassSymbol)(implicit ctx: Context) = - if (previous != unpickled) { - output("before-pickling.txt", previous) - output("after-pickling.txt", unpickled) - ctx.error(i"""pickling difference for ${cls.fullName} in ${cls.sourceFile}, for details: - | - | diff before-pickling.txt after-pickling.txt""") - } -} diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala deleted file mode 100644 index 1ed47d92e..000000000 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ /dev/null @@ -1,286 +0,0 @@ -package dotty.tools.dotc -package transform - -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} -import dotty.tools.dotc.ast.{Trees, tpd} -import scala.collection.{ mutable, immutable } -import ValueClasses._ -import scala.annotation.tailrec -import core._ -import typer.ErrorReporting._ -import typer.Checking -import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ -import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ -import util.Positions._ -import Decorators._ -import config.Printers.typr -import Symbols._, TypeUtils._ - -/** A macro transform that runs immediately after typer and that performs the following functions: - * - * (1) Add super accessors and protected accessors (@see SuperAccessors) - * - * (2) Convert parameter fields that have the same name as a corresponding - * public parameter field in a superclass to a forwarder to the superclass - * field (corresponding = super class field is initialized with subclass field) - * (@see ForwardParamAccessors) - * - * (3) Add synthetic methods (@see SyntheticMethods) - * - * (4) Check that `New` nodes can be instantiated, and that annotations are valid - * - * (5) Convert all trees representing types to TypeTrees. - * - * (6) Check the bounds of AppliedTypeTrees - * - * (7) Insert `.package` for selections of package object members - * - * (8) Replaces self references by name with `this` - * - * (9) Adds SourceFile annotations to all top-level classes and objects - * - * (10) Adds Child annotations to all sealed classes - * - * (11) Minimizes `call` fields of `Inline` nodes to just point to the toplevel - * class from which code was inlined. - * - * The reason for making this a macro transform is that some functions (in particular - * super and protected accessors and instantiation checks) are naturally top-down and - * don't lend themselves to the bottom-up approach of a mini phase. The other two functions - * (forwarding param accessors and synthetic methods) only apply to templates and fit - * mini-phase or subfunction of a macro phase equally well. But taken by themselves - * they do not warrant their own group of miniphases before pickling. - */ -class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTransformer => - - import tpd._ - - /** the following two members override abstract members in Transform */ - override def phaseName: String = "posttyper" - - override def transformPhase(implicit ctx: Context) = thisTransformer.next - - protected def newTransformer(implicit ctx: Context): Transformer = - new PostTyperTransformer - - val superAcc = new SuperAccessors(thisTransformer) - val paramFwd = new ParamForwarding(thisTransformer) - val synthMth = new SyntheticMethods(thisTransformer) - - private def newPart(tree: Tree): Option[New] = methPart(tree) match { - case Select(nu: New, _) => Some(nu) - case _ => None - } - - private def checkValidJavaAnnotation(annot: Tree)(implicit ctx: Context): Unit = { - // TODO fill in - } - - /** If the type of `tree` is a TermRefWithSignature with an underdefined - * signature, narrow the type by re-computing the signature (which should - * be fully-defined by now). - */ - private def fixSignature[T <: Tree](tree: T)(implicit ctx: Context): T = tree.tpe match { - case tpe: TermRefWithSignature if tpe.signature.isUnderDefined => - typr.println(i"fixing $tree with type ${tree.tpe.widen.toString} with sig ${tpe.signature} to ${tpe.widen.signature}") - tree.withType(TermRef.withSig(tpe.prefix, tpe.name, tpe.widen.signature)).asInstanceOf[T] - case _ => tree - } - - class PostTyperTransformer extends Transformer { - - private var inJavaAnnot: Boolean = false - - private var parentNews: Set[New] = Set() - - private def transformAnnot(annot: Tree)(implicit ctx: Context): Tree = { - val saved = inJavaAnnot - inJavaAnnot = annot.symbol is JavaDefined - if (inJavaAnnot) checkValidJavaAnnotation(annot) - try transform(annot) - finally inJavaAnnot = saved - } - - private def transformAnnot(annot: Annotation)(implicit ctx: Context): Annotation = - annot.derivedAnnotation(transformAnnot(annot.tree)) - - private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = { - val sym = tree.symbol - sym.transformAnnotations(transformAnnot) - if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) { - val info1 = Checking.checkNoPrivateLeaks(sym, tree.pos) - if (info1 ne sym.info) - sym.copySymDenotation(info = info1).installAfter(thisTransformer) - } - } - - private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { - val qual = tree.qualifier - qual.symbol.moduleClass.denot match { - case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) => - transformSelect(cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name), targs) - case _ => - val tree1 = super.transform(tree) - constToLiteral(tree1) match { - case _: Literal => tree1 - case _ => superAcc.transformSelect(tree1, targs) - } - } - } - - private def normalizeTypeArgs(tree: TypeApply)(implicit ctx: Context): TypeApply = tree.tpe match { - case pt: PolyType => // wait for more arguments coming - tree - case _ => - def decompose(tree: TypeApply): (Tree, List[Tree]) = tree.fun match { - case fun: TypeApply => - val (tycon, args) = decompose(fun) - (tycon, args ++ tree.args) - case _ => - (tree.fun, tree.args) - } - def reorderArgs(pnames: List[Name], namedArgs: List[NamedArg], otherArgs: List[Tree]): List[Tree] = pnames match { - case pname :: pnames1 => - namedArgs.partition(_.name == pname) match { - case (NamedArg(_, arg) :: _, namedArgs1) => - arg :: reorderArgs(pnames1, namedArgs1, otherArgs) - case _ => - val otherArg :: otherArgs1 = otherArgs - otherArg :: reorderArgs(pnames1, namedArgs, otherArgs1) - } - case nil => - assert(namedArgs.isEmpty && otherArgs.isEmpty) - Nil - } - val (tycon, args) = decompose(tree) - tycon.tpe.widen match { - case tp: PolyType => - val (namedArgs, otherArgs) = args.partition(isNamedArg) - val args1 = reorderArgs(tp.paramNames, namedArgs.asInstanceOf[List[NamedArg]], otherArgs) - TypeApply(tycon, args1).withPos(tree.pos).withType(tree.tpe) - case _ => - tree - } - } - - override def transform(tree: Tree)(implicit ctx: Context): Tree = - try tree match { - case tree: Ident if !tree.isType => - tree.tpe match { - case tpe: ThisType => This(tpe.cls).withPos(tree.pos) - case _ => paramFwd.adaptRef(fixSignature(tree)) - } - case tree @ Select(qual, name) => - if (name.isTypeName) { - Checking.checkRealizable(qual.tpe, qual.pos.focus) - super.transform(tree) - } - else - transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil) - case tree: Super => - if (ctx.owner.enclosingMethod.isInlineMethod) - ctx.error(em"super not allowed in inline ${ctx.owner}", tree.pos) - super.transform(tree) - case tree: TypeApply => - val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) - Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType]) - fn match { - case sel: Select => - val args1 = transform(args) - val sel1 = transformSelect(sel, args1) - if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree1)(sel1, args1) - case _ => - super.transform(tree1) - } - case tree @ Assign(sel: Select, _) => - superAcc.transformAssign(super.transform(tree)) - case Inlined(call, bindings, expansion) => - // Leave only a call trace consisting of - // - a reference to the top-level class from which the call was inlined, - // - the call's position - // in the call field of an Inlined node. - // The trace has enough info to completely reconstruct positions. - // The minimization is done for two reasons: - // 1. To save space (calls might contain large inline arguments, which would otherwise - // be duplicated - // 2. To enable correct pickling (calls can share symbols with the inlined code, which - // would trigger an assertion when pickling). - val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)) - case tree: Template => - val saved = parentNews - parentNews ++= tree.parents.flatMap(newPart) - try { - val templ1 = paramFwd.forwardParamAccessors(tree) - synthMth.addSyntheticMethods( - superAcc.wrapTemplate(templ1)( - super.transform(_).asInstanceOf[Template])) - } - finally parentNews = saved - case tree: DefDef => - transformMemberDef(tree) - superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef]) - case tree: TypeDef => - transformMemberDef(tree) - val sym = tree.symbol - if (sym.isClass) { - // Add SourceFile annotation to top-level classes - if (sym.owner.is(Package) && - ctx.compilationUnit.source.exists && - sym != defn.SourceFileAnnot) - sym.addAnnotation(Annotation.makeSourceFile(ctx.compilationUnit.source.file.path)) - - // Add Child annotation to sealed parents unless current class is anonymous - if (!sym.isAnonymousClass) // ignore anonymous class - for (parent <- sym.asClass.classInfo.classParents) { - val pclazz = parent.classSymbol - if (pclazz.is(Sealed)) pclazz.addAnnotation(Annotation.makeChild(sym)) - } - - tree - } - super.transform(tree) - case tree: MemberDef => - transformMemberDef(tree) - super.transform(tree) - case tree: New if !inJavaAnnot && !parentNews.contains(tree) => - Checking.checkInstantiable(tree.tpe, tree.pos) - super.transform(tree) - case tree @ Annotated(annotated, annot) => - cpy.Annotated(tree)(transform(annotated), transformAnnot(annot)) - case tree: AppliedTypeTree => - Checking.checkAppliedType(tree) - super.transform(tree) - case SingletonTypeTree(ref) => - Checking.checkRealizable(ref.tpe, ref.pos.focus) - super.transform(tree) - case tree: TypeTree => - tree.withType( - tree.tpe match { - case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot)) - case tpe => tpe - } - ) - case Import(expr, selectors) => - val exprTpe = expr.tpe - def checkIdent(ident: Ident): Unit = { - val name = ident.name.asTermName.encode - if (name != nme.WILDCARD && !exprTpe.member(name).exists && !exprTpe.member(name.toTypeName).exists) - ctx.error(s"${ident.name} is not a member of ${expr.show}", ident.pos) - } - selectors.foreach { - case ident: Ident => checkIdent(ident) - case Thicket((ident: Ident) :: _) => checkIdent(ident) - case _ => - } - super.transform(tree) - case tree => - super.transform(tree) - } - catch { - case ex : AssertionError => - println(i"error while transforming $tree") - throw ex - } - } -} diff --git a/src/dotty/tools/dotc/transform/PrivateToStatic.scala.disabled b/src/dotty/tools/dotc/transform/PrivateToStatic.scala.disabled deleted file mode 100644 index 218839d01..000000000 --- a/src/dotty/tools/dotc/transform/PrivateToStatic.scala.disabled +++ /dev/null @@ -1,94 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.SymTransformer -import Contexts.Context -import Symbols._ -import Scopes._ -import Flags._ -import StdNames._ -import SymDenotations._ -import Types._ -import collection.mutable -import TreeTransforms._ -import Decorators._ -import ast.Trees._ -import TreeTransforms.TransformerInfo - -/** Makes private methods static, provided they not deferred, accessors, or static, - * by rewriting a method `m` in class `C` as follows: - * - * private def m(ps) = e - * - * --> private static def($this: C, ps) = [this -> $this] e - */ -class PrivateToStatic extends MiniPhase with SymTransformer { thisTransform => - import ast.tpd._ - override def phaseName = "privateToStatic" - override def relaxedTyping = true - - private val Immovable = Deferred | Accessor | JavaStatic - - def shouldBeStatic(sd: SymDenotation)(implicit ctx: Context) = - sd.current(ctx.withPhase(thisTransform)).asSymDenotation - .is(PrivateMethod, butNot = Immovable) && - sd.owner.is(Trait) - - override def transformSym(sd: SymDenotation)(implicit ctx: Context): SymDenotation = - if (shouldBeStatic(sd)) { - val mt @ MethodType(pnames, ptypes) = sd.info - sd.copySymDenotation( - initFlags = sd.flags | JavaStatic, - info = MethodType(nme.SELF :: pnames, sd.owner.thisType :: ptypes, mt.resultType)) - } - else sd - - val treeTransform = new Transform(NoSymbol) - - class Transform(thisParam: Symbol) extends TreeTransform { - def phase = thisTransform - - override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = - if (shouldBeStatic(tree.symbol)) { - val selfParam = ctx.newSymbol(tree.symbol, nme.SELF, Param, tree.symbol.owner.thisType, coord = tree.pos) - new Transform(selfParam) - } - else this - - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = - if (shouldBeStatic(tree.symbol)) { - val thisParamDef = ValDef(thisParam.asTerm) - val vparams :: Nil = tree.vparamss - cpy.DefDef(tree)(vparamss = (thisParamDef :: vparams) :: Nil) - } - else tree - - override def transformThis(tree: This)(implicit ctx: Context, info: TransformerInfo) = - if (shouldBeStatic(ctx.owner.enclosingMethod)) ref(thisParam).withPos(tree.pos) - else tree - - /** Rwrites a call to a method `m` which is made static as folows: - * - * qual.m(args) --> m(qual, args) - */ - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = - tree.fun match { - case fun @ Select(qual, name) if shouldBeStatic(fun.symbol) => - ctx.debuglog(i"mapping $tree to ${cpy.Ident(fun)(name)} (${qual :: tree.args}%, %)") - cpy.Apply(tree)(ref(fun.symbol).withPos(fun.pos), qual :: tree.args) - case _ => - tree - } - - override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo) = - tree.meth match { - case meth @ Select(qual, name) if shouldBeStatic(meth.symbol) => - cpy.Closure(tree)( - env = qual :: tree.env, - meth = ref(meth.symbol).withPos(meth.pos)) - case _ => - tree - } - } -} diff --git a/src/dotty/tools/dotc/transform/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala deleted file mode 100644 index e718a7e60..000000000 --- a/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ /dev/null @@ -1,115 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TreeTransforms._ -import Contexts.Context -import Flags._ -import SymUtils._ -import Symbols._ -import SymDenotations._ -import Types._ -import Decorators._ -import DenotTransformers._ -import StdNames._ -import NameOps._ -import ast.Trees._ -import util.Positions._ -import Names._ -import collection.mutable -import ResolveSuper._ - -/** This phase adds super accessors and method overrides where - * linearization differs from Java's rule for default methods in interfaces. - * In particular: - * - * For every trait M directly implemented by the class (see SymUtils.mixin), in - * reverse linearization order, add the following definitions to C: - * - * 3.1 (done in `superAccessors`) For every superAccessor - * `<mods> def super$f[Ts](ps1)...(psN): U` in M: - * - * <mods> def super$f[Ts](ps1)...(psN): U = super[S].f[Ts](ps1)...(psN) - * - * where `S` is the superclass of `M` in the linearization of `C`. - * - * 3.2 (done in `methodOverrides`) For every method - * `<mods> def f[Ts](ps1)...(psN): U` in M` that needs to be disambiguated: - * - * <mods> def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) - * - * A method in M needs to be disambiguated if it is concrete, not overridden in C, - * and if it overrides another concrete method. - * - * This is the first part of what was the mixin phase. It is complemented by - * Mixin, which runs after erasure. - */ -class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - override def phaseName: String = "resolveSuper" - - override def runsAfter = Set(classOf[ElimByName], // verified empirically, need to figure out what the reason is. - classOf[AugmentScala2Traits]) - - override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { - val cls = impl.symbol.owner.asClass - val ops = new MixinOps(cls, thisTransform) - import ops._ - - def superAccessors(mixin: ClassSymbol): List[Tree] = - for (superAcc <- mixin.info.decls.filter(_ is SuperAccessor).toList) - yield polyDefDef(implementation(superAcc.asTerm), forwarder(rebindSuper(cls, superAcc))) - - def methodOverrides(mixin: ClassSymbol): List[Tree] = - for (meth <- mixin.info.decls.toList if needsForwarder(meth)) - yield polyDefDef(implementation(meth.asTerm), forwarder(meth)) - - val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin)) - - cpy.Template(impl)(body = overrides ::: impl.body) - } - - override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) = { - val meth = ddef.symbol.asTerm - if (meth.is(SuperAccessor, butNot = Deferred)) { - assert(ddef.rhs.isEmpty) - val cls = meth.owner.asClass - val ops = new MixinOps(cls, thisTransform) - import ops._ - polyDefDef(meth, forwarder(rebindSuper(cls, meth))) - } - else ddef - } - - private val PrivateOrAccessorOrDeferred = Private | Accessor | Deferred -} - -object ResolveSuper{ - /** Returns the symbol that is accessed by a super-accessor in a mixin composition. - * - * @param base The class in which everything is mixed together - * @param acc The symbol statically referred to by the superaccessor in the trait - */ - def rebindSuper(base: Symbol, acc: Symbol)(implicit ctx: Context): Symbol = { - var bcs = base.info.baseClasses.dropWhile(acc.owner != _).tail - var sym: Symbol = NoSymbol - val unexpandedAccName = - if (acc.is(ExpandedName)) // Cannot use unexpandedName because of #765. t2183.scala would fail if we did. - acc.name - .drop(acc.name.indexOfSlice(nme.EXPAND_SEPARATOR ++ nme.SUPER_PREFIX)) - .drop(nme.EXPAND_SEPARATOR.length) - else acc.name - val SuperAccessorName(memberName) = unexpandedAccName: Name // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type - ctx.debuglog(i"starting rebindsuper from $base of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") - while (bcs.nonEmpty && sym == NoSymbol) { - val other = bcs.head.info.nonPrivateDecl(memberName) - if (ctx.settings.debug.value) - ctx.log(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}") - sym = other.matchingDenotation(base.thisType, base.thisType.memberInfo(acc)).symbol - bcs = bcs.tail - } - assert(sym.exists) - sym - } -} diff --git a/src/dotty/tools/dotc/transform/RestoreScopes.scala b/src/dotty/tools/dotc/transform/RestoreScopes.scala deleted file mode 100644 index 8b9d2be0d..000000000 --- a/src/dotty/tools/dotc/transform/RestoreScopes.scala +++ /dev/null @@ -1,67 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.IdentityDenotTransformer -import Contexts.Context -import Symbols._ -import Scopes._ -import collection.mutable -import TreeTransforms.MiniPhaseTransform -import SymDenotations._ -import ast.Trees._ -import NameOps._ -import TreeTransforms.TransformerInfo -import StdNames._ - -/** The preceding lambda lift and flatten phases move symbols to different scopes - * and rename them. This miniphase cleans up afterwards and makes sure that all - * class scopes contain the symbols defined in them. - */ -class RestoreScopes extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - override def phaseName = "restoreScopes" - - /* Note: We need to wait until we see a package definition because - * DropEmptyConstructors changes template members when analyzing the - * enclosing package definitions. So by the time RestoreScopes gets to - * see a typedef or template, it still might be changed by DropEmptyConstructors. - */ - override def transformPackageDef(pdef: PackageDef)(implicit ctx: Context, info: TransformerInfo) = { - pdef.stats.foreach(restoreScope) - pdef - } - - private def restoreScope(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { - case TypeDef(_, impl: Template) => - val restoredDecls = newScope - for (stat <- impl.constr :: impl.body) - if (stat.isInstanceOf[MemberDef] && stat.symbol.exists) - restoredDecls.enter(stat.symbol) - // Enter class in enclosing package scope, in case it was an inner class before flatten. - // For top-level classes this does nothing. - val cls = tree.symbol.asClass - val pkg = cls.owner.asClass - - // Bring back companion links - val companionClass = cls.info.decls.lookup(nme.COMPANION_CLASS_METHOD) - val companionModule = cls.info.decls.lookup(nme.COMPANION_MODULE_METHOD) - - if (companionClass.exists) { - restoredDecls.enter(companionClass) - } - - if (companionModule.exists) { - restoredDecls.enter(companionModule) - } - - pkg.enter(cls) - val cinfo = cls.classInfo - tree.symbol.copySymDenotation( - info = cinfo.derivedClassInfo( // Dotty deviation: Cannot expand cinfo inline without a type error - decls = restoredDecls: Scope)).installAfter(thisTransform) - tree - case tree => tree - } -} - diff --git a/src/dotty/tools/dotc/transform/SelectStatic.scala b/src/dotty/tools/dotc/transform/SelectStatic.scala deleted file mode 100644 index 5d60bb984..000000000 --- a/src/dotty/tools/dotc/transform/SelectStatic.scala +++ /dev/null @@ -1,56 +0,0 @@ -package dotty.tools.dotc -package transform - -import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer -import dotty.tools.dotc.core.Flags._ -import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.core._ -import dotty.tools.dotc.transform.TreeTransforms._ - -/** Removes selects that would be compiled into GetStatic - * otherwise backend needs to be aware that some qualifiers need to be dropped. - * Similar transformation seems to be performed by flatten in nsc - * @author Dmytro Petrashko - */ -class SelectStatic extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - override def phaseName: String = "selectStatic" - - override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - val sym = tree.symbol - def isStaticMember = - (sym is Flags.Module) && sym.initial.maybeOwner.initial.isStaticOwner || - (sym is Flags.JavaStatic) || - (sym.maybeOwner is Flags.ImplClass) || - sym.hasAnnotation(ctx.definitions.ScalaStaticAnnot) - val isStaticRef = !sym.is(Package) && !sym.maybeOwner.is(Package) && isStaticMember - val tree1 = - if (isStaticRef && !tree.qualifier.symbol.is(JavaModule) && !tree.qualifier.isType) - Block(List(tree.qualifier), ref(sym)) - else tree - - normalize(tree1) - } - - private def normalize(t: Tree)(implicit ctx: Context) = t match { - case Select(Block(stats, qual), nm) => - Block(stats, cpy.Select(t)(qual, nm)) - case Apply(Block(stats, qual), nm) => - Block(stats, Apply(qual, nm)) - case TypeApply(Block(stats, qual), nm) => - Block(stats, TypeApply(qual, nm)) - case _ => t - } - - override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - normalize(tree) - } - - override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - normalize(tree) - } -} diff --git a/src/dotty/tools/dotc/transform/SeqLiterals.scala b/src/dotty/tools/dotc/transform/SeqLiterals.scala deleted file mode 100644 index 49ea69530..000000000 --- a/src/dotty/tools/dotc/transform/SeqLiterals.scala +++ /dev/null @@ -1,48 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Types._ -import dotty.tools.dotc.transform.TreeTransforms._ -import Contexts.Context -import Symbols._ -import Phases._ -import Decorators._ - -/** A transformer that eliminates SeqLiteral's, transforming `SeqLiteral(elems)` to an operation - * equivalent to - * - * JavaSeqLiteral(elems).toSeq - * - * Instead of `toSeq`, which takes an implicit, the appropriate "wrapArray" method - * is called directly. The reason for this step is that JavaSeqLiterals, being arrays - * keep a precise type after erasure, whereas SeqLiterals only get the erased type `Seq`, - */ -class SeqLiterals extends MiniPhaseTransform { - import ast.tpd._ - - override def phaseName = "seqLiterals" - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PatternMatcher]) - - override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match { - case tpd: SeqLiteral => assert(tpd.isInstanceOf[JavaSeqLiteral]) - case _ => - } - - override def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { - case tree: JavaSeqLiteral => tree - case _ => - val arr = JavaSeqLiteral(tree.elems, tree.elemtpt) - //println(i"trans seq $tree, arr = $arr: ${arr.tpe} ${arr.tpe.elemType}") - val elemtp = tree.elemtpt.tpe - val elemCls = elemtp.classSymbol - val (wrapMethStr, targs) = - if (elemCls.isPrimitiveValueClass) (s"wrap${elemCls.name}Array", Nil) - else if (elemtp derivesFrom defn.ObjectClass) ("wrapRefArray", elemtp :: Nil) - else ("genericWrapArray", elemtp :: Nil) - ref(defn.ScalaPredefModule) - .select(wrapMethStr.toTermName) - .appliedToTypes(targs) - .appliedTo(arr) - } -} diff --git a/src/dotty/tools/dotc/transform/Splitter.scala b/src/dotty/tools/dotc/transform/Splitter.scala deleted file mode 100644 index d62be1a82..000000000 --- a/src/dotty/tools/dotc/transform/Splitter.scala +++ /dev/null @@ -1,121 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import ast.Trees._ -import core._ -import Contexts._, Types._, Decorators._, Denotations._, Symbols._, SymDenotations._, Names._ - -/** Distribute applications into Block and If nodes - */ -class Splitter extends MiniPhaseTransform { thisTransform => - import ast.tpd._ - - override def phaseName: String = "splitter" - - /** Distribute arguments among splitted branches */ - def distribute(tree: GenericApply[Type], rebuild: (Tree, List[Tree]) => Context => Tree)(implicit ctx: Context) = { - def recur(fn: Tree): Tree = fn match { - case Block(stats, expr) => Block(stats, recur(expr)) - case If(cond, thenp, elsep) => If(cond, recur(thenp), recur(elsep)) - case _ => rebuild(fn, tree.args)(ctx) withPos tree.pos - } - recur(tree.fun) - } - - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) = - distribute(tree, typeApply) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = - distribute(tree, apply) - - private val typeApply = (fn: Tree, args: List[Tree]) => (ctx: Context) => TypeApply(fn, args)(ctx) - private val apply = (fn: Tree, args: List[Tree]) => (ctx: Context) => Apply(fn, args)(ctx) - -/* The following is no longer necessary, since we select members on the join of an or type: - * - /** If we select a name, make sure the node has a symbol. - * If necessary, split the qualifier with type tests. - * Example: Assume: - * - * class A { def f(x: S): T } - * class B { def f(x: S): T } - * def p(): A | B - * - * Then p().f(a) translates to - * - * val ev$1 = p() - * if (ev$1.isInstanceOf[A]) ev$1.asInstanceOf[A].f(a) - * else ev$1.asInstanceOf[B].f(a) - */ - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = { - val Select(qual, name) = tree - - def memberDenot(tp: Type): SingleDenotation = { - val mbr = tp.member(name) - if (!mbr.isOverloaded) mbr.asSingleDenotation - else tree.tpe match { - case tref: TermRefWithSignature => mbr.atSignature(tref.sig).checkUnique - case _ => - def alts = mbr.alternatives.map(alt => i"$alt: ${alt.info}").mkString(", ") - ctx.error(s"cannot disambiguate overloaded members $alts", tree.pos) - NoDenotation - } - } - - def candidates(tp: Type): List[Symbol] = { - val mbr = memberDenot(tp) - if (mbr.symbol.exists) mbr.symbol :: Nil - else tp.widen match { - case tref: TypeRef => - tref.info match { - case TypeBounds(_, hi) => candidates(hi) - case _ => Nil - } - case OrType(tp1, tp2) => - candidates(tp1) | candidates(tp2) - case AndType(tp1, tp2) => - candidates(tp1) & candidates(tp2) - case tpw => - Nil - } - } - - def isStructuralSelect(tp: Type): Boolean = tp.stripTypeVar match { - case tp: RefinedType => tp.refinedName == name || isStructuralSelect(tp.parent) - case tp: TypeProxy => isStructuralSelect(tp.underlying) - case AndType(tp1, tp2) => isStructuralSelect(tp1) || isStructuralSelect(tp2) - case _ => false - } - - if (tree.symbol.exists) tree - else { - def choose(qual: Tree, syms: List[Symbol]): Tree = { - def testOrCast(which: Symbol, mbr: Symbol) = - qual.select(which).appliedToType(mbr.owner.typeRef) - def select(sym: Symbol) = { - val qual1 = - if (qual.tpe derivesFrom sym.owner) qual - else testOrCast(defn.Any_asInstanceOf, sym) - qual1.select(sym).withPos(tree.pos) - } - syms match { - case Nil => - def msg = - if (isStructuralSelect(qual.tpe)) - s"cannot access member '$name' from structural type ${qual.tpe.widen.show}; use Dynamic instead" - else - s"no candidate symbols for ${tree.tpe.show} found in ${qual.tpe.show}" - ctx.error(msg, tree.pos) - tree - case sym :: Nil => - select(sym) - case sym :: syms1 => - If(testOrCast(defn.Any_isInstanceOf, sym), select(sym), choose(qual, syms1)) - } - } - evalOnce(qual)(qual => choose(qual, candidates(qual.tpe))) - } - } -*/ -} diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala deleted file mode 100644 index fea478c9b..000000000 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ /dev/null @@ -1,424 +0,0 @@ -package dotty.tools.dotc -package transform - -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} -import dotty.tools.dotc.ast.{Trees, tpd} -import scala.collection.{ mutable, immutable } -import ValueClasses._ -import scala.annotation.tailrec -import core._ -import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ -import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ -import util.Positions._ -import Decorators._ -import Symbols._, TypeUtils._ - -/** This class performs the following functions: - * - * (1) Adds super accessors for all super calls that either - * appear in a trait or have as a target a member of some outer class. - * - * (2) Adds protected accessors if the access to the protected member happens - * in a class which is not a subclass of the member's owner. - * - * It also checks that: - * - * (1) Symbols accessed from super are not abstract, or are overridden by - * an abstract override. - * - * (2) If a symbol accessed from super is defined in a real class (not a trait), - * there are no abstract members which override this member in Java's rules - * (see SI-4989; such an access would lead to illegal bytecode) - * - * (3) Super calls do not go to some synthetic members of Any (see isDisallowed) - * - * (4) Super calls do not go to synthetic field accessors - */ -class SuperAccessors(thisTransformer: DenotTransformer) { - - import tpd._ - - - /** Some parts of trees will get a new owner in subsequent phases. - * These are value class methods, which will become extension methods. - * (By-name arguments used to be included also, but these - * don't get a new class anymore, they are just wrapped in a new method). - * - * These regions will have to be treated specially for the purpose - * of adding accessors. For instance, super calls from these regions - * always have to go through an accessor. - * - * The `invalidEnclClass` field, if different from NoSymbol, - * contains the symbol that is not a valid owner. - */ - private var invalidEnclClass: Symbol = NoSymbol - - private def withInvalidCurrentClass[A](trans: => A)(implicit ctx: Context): A = { - val saved = invalidEnclClass - invalidEnclClass = ctx.owner - try trans - finally invalidEnclClass = saved - } - - private def validCurrentClass(implicit ctx: Context): Boolean = - ctx.owner.enclosingClass != invalidEnclClass - - /** List buffers for new accessor definitions, indexed by class */ - private val accDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() - - /** A super accessor call corresponding to `sel` */ - private def superAccessorCall(sel: Select)(implicit ctx: Context) = { - val Select(qual, name) = sel - val sym = sel.symbol - val clazz = qual.symbol.asClass - var supername = name.superName - if (clazz is Trait) supername = supername.expandedName(clazz) - - val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse { - ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz") - val deferredOrPrivate = if (clazz is Trait) Deferred | ExpandedName else Private - val acc = ctx.newSymbol( - clazz, supername, SuperAccessor | Artifact | Method | deferredOrPrivate, - sel.tpe.widenSingleton.ensureMethodic, coord = sym.coord).enteredAfter(thisTransformer) - // Diagnostic for SI-7091 - if (!accDefs.contains(clazz)) - ctx.error(s"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz is Package}. Accessor required for ${sel} (${sel.show})", sel.pos) - else accDefs(clazz) += DefDef(acc, EmptyTree) - acc - } - - This(clazz).select(superAcc).withPos(sel.pos) - } - - /** Check selection `super.f` for conforming to rules. If necessary, - * replace by a super accessor call. - */ - private def transformSuperSelect(sel: Select)(implicit ctx: Context): Tree = { - val Select(sup @ Super(_, mix), name) = sel - val sym = sel.symbol - assert(sup.symbol.exists, s"missing symbol in $sel: ${sup.tpe}") - val clazz = sup.symbol.asClass - - if (sym.isTerm && !sym.is(Method, butNot = Accessor) && !ctx.owner.is(ParamForwarder)) - // ParamForwaders as installed ParamForwarding.scala do use super calls to vals - ctx.error(s"super may be not be used on ${sym.underlyingSymbol}", sel.pos) - else if (isDisallowed(sym)) - ctx.error(s"super not allowed here: use this.${sel.name.decode} instead", sel.pos) - else if (sym is Deferred) { - val member = sym.overridingSymbol(clazz) - if (!mix.name.isEmpty || - !member.exists || - !((member is AbsOverride) && member.isIncompleteIn(clazz))) - ctx.error( - i"${sym.showLocated} is accessed from super. It may not be abstract unless it is overridden by a member declared `abstract' and `override'", - sel.pos) - else ctx.log(i"ok super $sel ${sym.showLocated} $member $clazz ${member.isIncompleteIn(clazz)}") - } - else if (mix.name.isEmpty && !(sym.owner is Trait)) - // SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract. - for (intermediateClass <- clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)) { - val overriding = sym.overridingSymbol(intermediateClass) - if ((overriding is (Deferred, butNot = AbsOverride)) && !(overriding.owner is Trait)) - ctx.error( - s"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract", - sel.pos) - - } - if (name.isTermName && mix.name.isEmpty && - ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentClass)) - superAccessorCall(sel)(ctx.withPhase(thisTransformer.next)) - else sel - } - - /** Disallow some super.XX calls targeting Any methods which would - * otherwise lead to either a compiler crash or runtime failure. - */ - private def isDisallowed(sym: Symbol)(implicit ctx: Context) = { - val d = defn - import d._ - (sym eq Any_isInstanceOf) || - (sym eq Any_asInstanceOf) || - (sym eq Any_==) || - (sym eq Any_!=) || - (sym eq Any_##) - } - - /** Replace `sel` (or `sel[targs]` if `targs` is nonempty) with a protected accessor - * call, if necessary. - */ - private def ensureProtectedAccessOK(sel: Select, targs: List[Tree])(implicit ctx: Context) = { - val sym = sel.symbol - if (sym.isTerm && !sel.name.isOuterSelect && needsProtectedAccessor(sym, sel.pos)) { - ctx.debuglog("Adding protected accessor for " + sel) - protectedAccessorCall(sel, targs) - } else sel - } - - /** Add a protected accessor, if needed, and return a tree that calls - * the accessor and returns the same member. The result is already - * typed. - */ - private def protectedAccessorCall(sel: Select, targs: List[Tree])(implicit ctx: Context): Tree = { - val Select(qual, _) = sel - val sym = sel.symbol.asTerm - val clazz = hostForAccessorOf(sym, currentClass) - assert(clazz.exists, sym) - ctx.debuglog("Decided for host class: " + clazz) - - val accName = sym.name.protectedAccessorName - - // if the result type depends on the this type of an enclosing class, the accessor - // has to take an object of exactly this type, otherwise it's more general - val receiverType = - if (isThisType(sym.info.finalResultType)) clazz.thisType - else clazz.classInfo.selfType - val accType = { - def accTypeOf(tpe: Type): Type = tpe match { - case tpe: PolyType => - tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType)) - case _ => - MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0))) - } - accTypeOf(sym.info) - } - val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse { - val newAcc = ctx.newSymbol( - clazz, accName, Artifact, accType, coord = sel.pos).enteredAfter(thisTransformer) - val code = polyDefDef(newAcc, trefs => vrefss => { - val (receiver :: _) :: tail = vrefss - val base = receiver.select(sym).appliedToTypes(trefs) - (base /: vrefss)(Apply(_, _)) - }) - ctx.debuglog("created protected accessor: " + code) - accDefs(clazz) += code - newAcc - } - val res = This(clazz) - .select(protectedAccessor) - .appliedToTypeTrees(targs) - .appliedTo(qual) - .withPos(sel.pos) - ctx.debuglog(s"Replaced $sel with $res") - res - } - - def isProtectedAccessor(tree: Tree)(implicit ctx: Context): Boolean = tree match { - case Apply(TypeApply(Select(_, name), _), qual :: Nil) => name.isProtectedAccessorName - case _ => false - } - - /** Add a protected accessor, if needed, and return a tree that calls - * the accessor and returns the same member. The result is already - * typed. - */ - private def protectedAccessor(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { - val Select(qual, _) = tree - val sym = tree.symbol.asTerm - val clazz = hostForAccessorOf(sym, currentClass) - assert(clazz.exists, sym) - ctx.debuglog("Decided for host class: " + clazz) - - val accName = sym.name.protectedAccessorName - - // if the result type depends on the this type of an enclosing class, the accessor - // has to take an object of exactly this type, otherwise it's more general - val receiverType = - if (isThisType(sym.info.finalResultType)) clazz.thisType - else clazz.classInfo.selfType - def accTypeOf(tpe: Type): Type = tpe match { - case tpe: PolyType => - tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType)) - case _ => - MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0))) - } - val accType = accTypeOf(sym.info) - val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse { - val newAcc = ctx.newSymbol( - clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisTransformer) - val code = polyDefDef(newAcc, trefs => vrefss => { - val (receiver :: _) :: tail = vrefss - val base = receiver.select(sym).appliedToTypes(trefs) - (base /: vrefss)(Apply(_, _)) - }) - ctx.debuglog("created protected accessor: " + code) - accDefs(clazz) += code - newAcc - } - val res = This(clazz) - .select(protectedAccessor) - .appliedToTypeTrees(targs) - .appliedTo(qual) - .withPos(tree.pos) - ctx.debuglog(s"Replaced $tree with $res") - res - } - - /** Add an accessor for field, if needed, and return a selection tree for it . - * The result is not typed. - */ - private def protectedSetter(tree: Select)(implicit ctx: Context): Tree = { - val field = tree.symbol.asTerm - val clazz = hostForAccessorOf(field, currentClass) - assert(clazz.exists, field) - ctx.debuglog("Decided for host class: " + clazz) - - val accName = field.name.protectedSetterName - val accType = MethodType(clazz.classInfo.selfType :: field.info :: Nil, defn.UnitType) - val protectedAccessor = clazz.info.decl(accName).symbol orElse { - val newAcc = ctx.newSymbol( - clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisTransformer) - val code = DefDef(newAcc, vrefss => { - val (receiver :: value :: Nil) :: Nil = vrefss - Assign(receiver.select(field), value).withPos(tree.pos) - }) - ctx.debuglog("created protected setter: " + code) - accDefs(clazz) += code - newAcc - } - This(clazz).select(protectedAccessor).withPos(tree.pos) - } - - /** Does `sym` need an accessor when accessed from `currentClass`? - * A special case arises for classes with explicit self-types. If the - * self type is a Java class, and a protected accessor is needed, we issue - * an error. If the self type is a Scala class, we don't add an accessor. - * An accessor is not needed if the access boundary is larger than the - * enclosing package, since that translates to 'public' on the host sys. - * (as Java has no real package nesting). - * - * If the access happens inside a 'trait', access is more problematic since - * the implementation code is moved to an '$class' class which does not - * inherit anything. Since we can't (yet) add accessors for 'required' - * classes, this has to be signaled as error. - * FIXME Need to better understand this logic - */ - private def needsProtectedAccessor(sym: Symbol, pos: Position)(implicit ctx: Context): Boolean = { - val clazz = currentClass - val host = hostForAccessorOf(sym, clazz) - val selfType = host.classInfo.selfType - def accessibleThroughSubclassing = - validCurrentClass && (selfType <:< sym.owner.typeRef) && !clazz.is(Trait) - - val isCandidate = ( - sym.is(Protected) - && sym.is(JavaDefined) - && !sym.effectiveOwner.is(Package) - && !accessibleThroughSubclassing - && (sym.enclosingPackageClass != currentClass.enclosingPackageClass) - && (sym.enclosingPackageClass == sym.accessBoundary(sym.enclosingPackageClass)) - ) - def isSelfType = !(host.typeRef <:< selfType) && { - if (selfType.typeSymbol.is(JavaDefined)) - ctx.restrictionError(s"cannot accesses protected $sym from within $clazz with self type $selfType", pos) - true - } - def isJavaProtected = host.is(Trait) && sym.is(JavaDefined) && { - ctx.restrictionError( - s"""$clazz accesses protected $sym inside a concrete trait method. - |Add an accessor in a class extending ${sym.enclosingClass} as a workaround.""".stripMargin, - pos - ) - true - } - isCandidate && !host.is(Package) && !isSelfType && !isJavaProtected - } - - /** Return the innermost enclosing class C of referencingClass for which either - * of the following holds: - * - C is a subclass of sym.owner or - * - C is declared in the same package as sym's owner - */ - private def hostForAccessorOf(sym: Symbol, referencingClass: ClassSymbol)(implicit ctx: Context): ClassSymbol = - if (referencingClass.derivesFrom(sym.owner) - || referencingClass.classInfo.selfType <:< sym.owner.typeRef - || referencingClass.enclosingPackageClass == sym.owner.enclosingPackageClass) { - assert(referencingClass.isClass, referencingClass) - referencingClass - } - else if (referencingClass.owner.enclosingClass.exists) - hostForAccessorOf(sym, referencingClass.owner.enclosingClass.asClass) - else - referencingClass - - /** Is 'tpe' a ThisType, or a type proxy with a ThisType as transitively underlying type? */ - private def isThisType(tpe: Type)(implicit ctx: Context): Boolean = tpe match { - case tpe: ThisType => !tpe.cls.is(PackageClass) - case tpe: TypeProxy => isThisType(tpe.underlying) - case _ => false - } - - /** Transform select node, adding super and protected accessors as needed */ - def transformSelect(tree: Tree, targs: List[Tree])(implicit ctx: Context) = { - val sel @ Select(qual, name) = tree - val sym = sel.symbol - qual match { - case _: This => - /* - * A trait which extends a class and accesses a protected member - * of that class cannot implement the necessary accessor method - * because its implementation is in an implementation class (e.g. - * Foo$class) which inherits nothing, and jvm access restrictions - * require the call site to be in an actual subclass. So non-trait - * classes inspect their ancestors for any such situations and - * generate the accessors. See SI-2296. - */ - // FIXME (from scalac's SuperAccessors) - // - this should be unified with needsProtectedAccessor, but some - // subtlety which presently eludes me is foiling my attempts. - val shouldEnsureAccessor = ( - (currentClass is Trait) - && (sym is Protected) - && sym.enclosingClass != currentClass - && !(sym.owner is PackageClass) // SI-7091 no accessor needed package owned (ie, top level) symbols - && !(sym.owner is Trait) - && sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass - && qual.symbol.info.member(sym.name).exists - && !needsProtectedAccessor(sym, sel.pos)) - if (shouldEnsureAccessor) { - ctx.log("Ensuring accessor for call to protected " + sym.showLocated + " from " + currentClass) - superAccessorCall(sel) - } else - ensureProtectedAccessOK(sel, targs) - - case Super(_, mix) => - transformSuperSelect(sel) - - case _ => - ensureProtectedAccessOK(sel, targs) - } - } - - /** Transform assignment, adding a protected setter if needed */ - def transformAssign(tree: Tree)(implicit ctx: Context) = { - val Assign(lhs @ Select(qual, name), rhs) = tree - if ((lhs.symbol is Mutable) && - (lhs.symbol is JavaDefined) && - needsProtectedAccessor(lhs.symbol, tree.pos)) { - ctx.debuglog("Adding protected setter for " + tree) - val setter = protectedSetter(lhs) - ctx.debuglog("Replaced " + tree + " with " + setter) - setter.appliedTo(qual, rhs) - } - else tree - } - - /** Wrap template to template transform `op` with needed initialization and finalization */ - def wrapTemplate(tree: Template)(op: Template => Template)(implicit ctx: Context) = { - accDefs(currentClass) = new mutable.ListBuffer[Tree] - val impl = op(tree) - val accessors = accDefs.remove(currentClass).get - if (accessors.isEmpty) impl - else { - val (params, rest) = impl.body span { - case td: TypeDef => !td.isClassDef - case vd: ValOrDefDef => vd.symbol.flags is ParamAccessor - case _ => false - } - cpy.Template(impl)(body = params ++ accessors ++ rest) - } - } - - /** Wrap `DefDef` producing operation `op`, potentially setting `invalidClass` info */ - def wrapDefDef(ddef: DefDef)(op: => DefDef)(implicit ctx: Context) = - if (isMethodWithExtension(ddef.symbol)) withInvalidCurrentClass(op) else op -} diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala deleted file mode 100644 index 05305575e..000000000 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ /dev/null @@ -1,117 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Types._ -import Contexts._ -import Symbols._ -import SymDenotations._ -import Decorators._ -import Names._ -import StdNames._ -import NameOps._ -import Flags._ -import Annotations._ -import language.implicitConversions - -object SymUtils { - implicit def decorateSymbol(sym: Symbol): SymUtils = new SymUtils(sym) - implicit def decorateSymDenot(d: SymDenotation): SymUtils = new SymUtils(d.symbol) -} - -/** A decorator that provides methods on symbols - * that are needed in the transformer pipeline. - */ -class SymUtils(val self: Symbol) extends AnyVal { - import SymUtils._ - - /** All traits implemented by a class or trait except for those inherited through the superclass. */ - def directlyInheritedTraits(implicit ctx: Context) = { - val superCls = self.asClass.superClass - val baseClasses = self.asClass.baseClasses - if (baseClasses.isEmpty) Nil - else baseClasses.tail.takeWhile(_ ne superCls).reverse - } - - /** All traits implemented by a class, except for those inherited through the superclass. - * The empty list if `self` is a trait. - */ - def mixins(implicit ctx: Context) = { - if (self is Trait) Nil - else directlyInheritedTraits - } - - def isTypeTestOrCast(implicit ctx: Context): Boolean = - self == defn.Any_asInstanceOf || self == defn.Any_isInstanceOf - - def isVolatile(implicit ctx: Context) = self.hasAnnotation(defn.VolatileAnnot) - - def isAnyOverride(implicit ctx: Context) = self.is(Override) || self.is(AbsOverride) - // careful: AbsOverride is a term only flag. combining with Override would catch only terms. - - /** If this is a constructor, its owner: otherwise this. */ - final def skipConstructor(implicit ctx: Context): Symbol = - if (self.isConstructor) self.owner else self - - /** The closest properly enclosing method or class of this symbol. */ - final def enclosure(implicit ctx: Context) = { - self.owner.enclosingMethodOrClass - } - - /** The closest enclosing method or class of this symbol */ - final def enclosingMethodOrClass(implicit ctx: Context): Symbol = - if (self.is(Method, butNot = Label) || self.isClass) self - else if (self.exists) self.owner.enclosingMethodOrClass - else NoSymbol - - /** Apply symbol/symbol substitution to this symbol */ - def subst(from: List[Symbol], to: List[Symbol]): Symbol = { - def loop(from: List[Symbol], to: List[Symbol]): Symbol = - if (from.isEmpty) self - else if (self eq from.head) to.head - else loop(from.tail, to.tail) - loop(from, to) - } - - def accessorNamed(name: TermName)(implicit ctx: Context): Symbol = - self.owner.info.decl(name).suchThat(_ is Accessor).symbol - - def termParamAccessors(implicit ctx: Context): List[Symbol] = - self.info.decls.filter(_ is TermParamAccessor).toList - - def caseAccessors(implicit ctx:Context) = - self.info.decls.filter(_ is CaseAccessor).toList - - def getter(implicit ctx: Context): Symbol = - if (self.isGetter) self else accessorNamed(self.asTerm.name.getterName) - - def setter(implicit ctx: Context): Symbol = - if (self.isSetter) self - else accessorNamed(self.asTerm.name.setterName) - - def field(implicit ctx: Context): Symbol = - self.owner.info.decl(self.asTerm.name.fieldName).suchThat(!_.is(Method)).symbol - - def isField(implicit ctx: Context): Boolean = - self.isTerm && !self.is(Method) - - def implClass(implicit ctx: Context): Symbol = - self.owner.info.decl(self.name.implClassName).symbol - - def annotationsCarrying(meta: ClassSymbol)(implicit ctx: Context): List[Annotation] = - self.annotations.filter(_.symbol.hasAnnotation(meta)) - - def withAnnotationsCarrying(from: Symbol, meta: ClassSymbol)(implicit ctx: Context): self.type = { - self.addAnnotations(from.annotationsCarrying(meta)) - self - } - - def registerCompanionMethod(name: Name, target: Symbol)(implicit ctx: Context) = { - if (!self.unforcedDecls.lookup(name).exists) { - val companionMethod = ctx.synthesizeCompanionMethod(name, target, self) - if (companionMethod.exists) { - companionMethod.entered - } - } - } -} diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala deleted file mode 100644 index 9dfd92fe9..000000000 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ /dev/null @@ -1,198 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._, SymUtils._ -import scala.collection.{ mutable, immutable } -import Flags._ -import TreeTransforms._ -import DenotTransformers._ -import ast.Trees._ -import ast.untpd -import Decorators._ -import NameOps._ -import ValueClasses.isDerivedValueClass -import scala.collection.mutable.ListBuffer -import scala.language.postfixOps - -/** Synthetic method implementations for case classes, case objects, - * and value classes. - * Selectively added to case classes/objects, unless a non-default - * implementation already exists: - * def equals(other: Any): Boolean - * def hashCode(): Int - * def canEqual(other: Any): Boolean - * def toString(): String - * def productArity: Int - * def productPrefix: String - * Special handling: - * protected def readResolve(): AnyRef - * - * Selectively added to value classes, unless a non-default - * implementation already exists: - * - * def equals(other: Any): Boolean - * def hashCode(): Int - */ -class SyntheticMethods(thisTransformer: DenotTransformer) { - import ast.tpd._ - - private var myValueSymbols: List[Symbol] = Nil - private var myCaseSymbols: List[Symbol] = Nil - - private def initSymbols(implicit ctx: Context) = - if (myValueSymbols.isEmpty) { - myValueSymbols = List(defn.Any_hashCode, defn.Any_equals) - myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, - defn.Product_productArity, defn.Product_productPrefix) - } - - def valueSymbols(implicit ctx: Context) = { initSymbols; myValueSymbols } - def caseSymbols(implicit ctx: Context) = { initSymbols; myCaseSymbols } - - /** The synthetic methods of the case or value class `clazz`. - */ - def syntheticMethods(clazz: ClassSymbol)(implicit ctx: Context): List[Tree] = { - val clazzType = clazz.typeRef - lazy val accessors = - if (isDerivedValueClass(clazz)) - clazz.termParamAccessors - else - clazz.caseAccessors - - val symbolsToSynthesize: List[Symbol] = - if (clazz.is(Case)) caseSymbols - else if (isDerivedValueClass(clazz)) valueSymbols - else Nil - - def syntheticDefIfMissing(sym: Symbol): List[Tree] = { - val existing = sym.matchingMember(clazz.thisType) - if (existing == sym || existing.is(Deferred)) syntheticDef(sym) :: Nil - else Nil - } - - def syntheticDef(sym: Symbol): Tree = { - val synthetic = sym.copy( - owner = clazz, - flags = sym.flags &~ Deferred | Synthetic | Override, - coord = clazz.coord).enteredAfter(thisTransformer).asTerm - - def forwardToRuntime(vrefss: List[List[Tree]]): Tree = - ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head) - - def ownName(vrefss: List[List[Tree]]): Tree = - Literal(Constant(clazz.name.stripModuleClassSuffix.decode.toString)) - - def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match { - case nme.hashCode_ if isDerivedValueClass(clazz) => vrefss => valueHashCodeBody - case nme.hashCode_ => vrefss => caseHashCodeBody - case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime - case nme.equals_ => vrefss => equalsBody(vrefss.head.head) - case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head) - case nme.productArity => vrefss => Literal(Constant(accessors.length)) - case nme.productPrefix => ownName - } - ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}") - DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))) - } - - /** The class - * - * case class C(x: T, y: U) - * - * gets the equals method: - * - * def equals(that: Any): Boolean = - * (this eq that) || { - * that match { - * case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y - * case _ => false - * } - * - * If C is a value class the initial `eq` test is omitted. - */ - def equalsBody(that: Tree)(implicit ctx: Context): Tree = { - val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0 - def wildcardAscription(tp: Type) = Typed(Underscore(tp), TypeTree(tp)) - val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C) - val comparisons = accessors map (accessor => - This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor))) - val rhs = // this.x == this$0.x && this.y == x$0.y - if (comparisons.isEmpty) Literal(Constant(true)) else comparisons.reduceLeft(_ and _) - val matchingCase = CaseDef(pattern, EmptyTree, rhs) // case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y - val defaultCase = CaseDef(wildcardAscription(defn.AnyType), EmptyTree, Literal(Constant(false))) // case _ => false - val matchExpr = Match(that, List(matchingCase, defaultCase)) - if (isDerivedValueClass(clazz)) matchExpr - else { - val eqCompare = This(clazz).select(defn.Object_eq).appliedTo(that.asInstance(defn.ObjectType)) - eqCompare or matchExpr - } - } - - /** The class - * - * class C(x: T) extends AnyVal - * - * gets the hashCode method: - * - * def hashCode: Int = x.hashCode() - */ - def valueHashCodeBody(implicit ctx: Context): Tree = { - assert(accessors.length == 1) - ref(accessors.head).select(nme.hashCode_).ensureApplied - } - - /** The class - * - * case class C(x: T, y: T) - * - * gets the hashCode method: - * - * def hashCode: Int = { - * <synthetic> var acc: Int = 0xcafebabe; - * acc = Statics.mix(acc, x); - * acc = Statics.mix(acc, Statics.this.anyHash(y)); - * Statics.finalizeHash(acc, 2) - * } - */ - def caseHashCodeBody(implicit ctx: Context): Tree = { - val acc = ctx.newSymbol(ctx.owner, "acc".toTermName, Mutable | Synthetic, defn.IntType, coord = ctx.owner.pos) - val accDef = ValDef(acc, Literal(Constant(0xcafebabe))) - val mixes = for (accessor <- accessors.toList) yield - Assign(ref(acc), ref(defn.staticsMethod("mix")).appliedTo(ref(acc), hashImpl(accessor))) - val finish = ref(defn.staticsMethod("finalizeHash")).appliedTo(ref(acc), Literal(Constant(accessors.size))) - Block(accDef :: mixes, finish) - } - - /** The hashCode implementation for given symbol `sym`. */ - def hashImpl(sym: Symbol)(implicit ctx: Context): Tree = - defn.scalaClassName(sym.info.finalResultType) match { - case tpnme.Unit | tpnme.Null => Literal(Constant(0)) - case tpnme.Boolean => If(ref(sym), Literal(Constant(1231)), Literal(Constant(1237))) - case tpnme.Int => ref(sym) - case tpnme.Short | tpnme.Byte | tpnme.Char => ref(sym).select(nme.toInt) - case tpnme.Long => ref(defn.staticsMethod("longHash")).appliedTo(ref(sym)) - case tpnme.Double => ref(defn.staticsMethod("doubleHash")).appliedTo(ref(sym)) - case tpnme.Float => ref(defn.staticsMethod("floatHash")).appliedTo(ref(sym)) - case _ => ref(defn.staticsMethod("anyHash")).appliedTo(ref(sym)) - } - - /** The class - * - * case class C(...) - * - * gets the canEqual method - * - * def canEqual(that: Any) = that.isInstanceOf[C] - */ - def canEqualBody(that: Tree): Tree = that.isInstance(clazzType) - - symbolsToSynthesize flatMap syntheticDefIfMissing - } - - def addSyntheticMethods(impl: Template)(implicit ctx: Context) = - if (ctx.owner.is(Case) || isDerivedValueClass(ctx.owner)) - cpy.Template(impl)(body = impl.body ++ syntheticMethods(ctx.owner.asClass)) - else - impl -} diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala deleted file mode 100644 index dc4454439..000000000 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ /dev/null @@ -1,384 +0,0 @@ -package dotty.tools.dotc.transform - -import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.ast.{TreeTypeMap, tpd} -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Decorators._ -import dotty.tools.dotc.core.DenotTransformers.DenotTransformer -import dotty.tools.dotc.core.Denotations.SingleDenotation -import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.core._ -import dotty.tools.dotc.transform.TailRec._ -import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} - -/** - * A Tail Rec Transformer - * @author Erik Stenman, Iulian Dragos, - * ported and heavily modified for dotty by Dmitry Petrashko - * @version 1.1 - * - * What it does: - * <p> - * Finds method calls in tail-position and replaces them with jumps. - * A call is in a tail-position if it is the last instruction to be - * executed in the body of a method. This is done by recursing over - * the trees that may contain calls in tail-position (trees that can't - * contain such calls are not transformed). However, they are not that - * many. - * </p> - * <p> - * Self-recursive calls in tail-position are replaced by jumps to a - * label at the beginning of the method. As the JVM provides no way to - * jump from a method to another one, non-recursive calls in - * tail-position are not optimized. - * </p> - * <p> - * A method call is self-recursive if it calls the current method and - * the method is final (otherwise, it could - * be a call to an overridden method in a subclass). - * - * Recursive calls on a different instance - * are optimized. Since 'this' is not a local variable it s added as - * a label parameter. - * </p> - * <p> - * This phase has been moved before pattern matching to catch more - * of the common cases of tail recursive functions. This means that - * more cases should be taken into account (like nested function, and - * pattern cases). - * </p> - * <p> - * If a method contains self-recursive calls, a label is added to at - * the beginning of its body and the calls are replaced by jumps to - * that label. - * </p> - * <p> - * - * In scalac, If the method had type parameters, the call must contain same - * parameters as type arguments. This is no longer case in dotc. - * In scalac, this is named tailCall but it does only provide optimization for - * self recursive functions, that's why it's renamed to tailrec - * </p> - */ -class TailRec extends MiniPhaseTransform with DenotTransformer with FullParameterization { thisTransform => - - import dotty.tools.dotc.ast.tpd._ - - override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref - - override def phaseName: String = "tailrec" - override def treeTransformPhase = thisTransform // TODO Make sure tailrec runs at next phase. - - final val labelPrefix = "tailLabel" - final val labelFlags = Flags.Synthetic | Flags.Label - - /** Symbols of methods that have @tailrec annotatios inside */ - private val methodsWithInnerAnnots = new collection.mutable.HashSet[Symbol]() - - override def transformUnit(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = { - methodsWithInnerAnnots.clear() - tree - } - - override def transformTyped(tree: Typed)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (tree.tpt.tpe.hasAnnotation(defn.TailrecAnnot)) - methodsWithInnerAnnots += ctx.owner.enclosingMethod - tree - } - - private def mkLabel(method: Symbol, abstractOverClass: Boolean)(implicit c: Context): TermSymbol = { - val name = c.freshName(labelPrefix) - - if (method.owner.isClass) - c.newSymbol(method, name.toTermName, labelFlags, fullyParameterizedType(method.info, method.enclosingClass.asClass, abstractOverClass, liftThisType = false)) - else c.newSymbol(method, name.toTermName, labelFlags, method.info) - } - - override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - val sym = tree.symbol - tree match { - case dd@DefDef(name, tparams, vparamss0, tpt, _) - if (sym.isEffectivelyFinal) && !((sym is Flags.Accessor) || (dd.rhs eq EmptyTree) || (sym is Flags.Label)) => - val mandatory = sym.hasAnnotation(defn.TailrecAnnot) - atGroupEnd { implicit ctx: Context => - - cpy.DefDef(dd)(rhs = { - - val defIsTopLevel = sym.owner.isClass - val origMeth = sym - val label = mkLabel(sym, abstractOverClass = defIsTopLevel) - val owner = ctx.owner.enclosingClass.asClass - val thisTpe = owner.thisType.widen - - var rewrote = false - - // Note: this can be split in two separate transforms(in different groups), - // than first one will collect info about which transformations and rewritings should be applied - // and second one will actually apply, - // now this speculatively transforms tree and throws away result in many cases - val rhsSemiTransformed = { - val transformer = new TailRecElimination(origMeth, dd.tparams, owner, thisTpe, mandatory, label, abstractOverClass = defIsTopLevel) - val rhs = atGroupEnd(transformer.transform(dd.rhs)(_)) - rewrote = transformer.rewrote - rhs - } - - if (rewrote) { - val dummyDefDef = cpy.DefDef(tree)(rhs = rhsSemiTransformed) - if (tree.symbol.owner.isClass) { - val labelDef = fullyParameterizedDef(label, dummyDefDef, abstractOverClass = defIsTopLevel) - val call = forwarder(label, dd, abstractOverClass = defIsTopLevel, liftThisType = true) - Block(List(labelDef), call) - } else { // inner method. Tail recursion does not change `this` - val labelDef = polyDefDef(label, trefs => vrefss => { - val origMeth = tree.symbol - val origTParams = tree.tparams.map(_.symbol) - val origVParams = tree.vparamss.flatten map (_.symbol) - new TreeTypeMap( - typeMap = identity(_) - .substDealias(origTParams, trefs) - .subst(origVParams, vrefss.flatten.map(_.tpe)), - oldOwners = origMeth :: Nil, - newOwners = label :: Nil - ).transform(rhsSemiTransformed) - }) - val callIntoLabel = ( - if (dd.tparams.isEmpty) ref(label) - else ref(label).appliedToTypes(dd.tparams.map(_.tpe)) - ).appliedToArgss(vparamss0.map(_.map(x=> ref(x.symbol)))) - Block(List(labelDef), callIntoLabel) - }} else { - if (mandatory) ctx.error( - "TailRec optimisation not applicable, method not tail recursive", - // FIXME: want to report this error on `dd.namePos`, but - // because of extension method getting a weird pos, it is - // better to report on symbol so there's no overlap - sym.pos - ) - dd.rhs - } - }) - } - case d: DefDef if d.symbol.hasAnnotation(defn.TailrecAnnot) || methodsWithInnerAnnots.contains(d.symbol) => - ctx.error("TailRec optimisation not applicable, method is neither private nor final so can be overridden", sym.pos) - d - case d if d.symbol.hasAnnotation(defn.TailrecAnnot) || methodsWithInnerAnnots.contains(d.symbol) => - ctx.error("TailRec optimisation not applicable, not a method", sym.pos) - d - case _ => tree - } - - } - - class TailRecElimination(method: Symbol, methTparams: List[Tree], enclosingClass: Symbol, thisType: Type, isMandatory: Boolean, label: Symbol, abstractOverClass: Boolean) extends tpd.TreeMap { - - import dotty.tools.dotc.ast.tpd._ - - var rewrote = false - - private val defaultReason = "it contains a recursive call not in tail position" - - private var ctx: TailContext = yesTailContext - - /** Rewrite this tree to contain no tail recursive calls */ - def transform(tree: Tree, nctx: TailContext)(implicit c: Context): Tree = { - if (ctx == nctx) transform(tree) - else { - val saved = ctx - ctx = nctx - try transform(tree) - finally this.ctx = saved - } - } - - def yesTailTransform(tree: Tree)(implicit c: Context): Tree = - transform(tree, yesTailContext) - - def noTailTransform(tree: Tree)(implicit c: Context): Tree = - transform(tree, noTailContext) - - def noTailTransforms[Tr <: Tree](trees: List[Tr])(implicit c: Context): List[Tr] = - trees.map(noTailTransform).asInstanceOf[List[Tr]] - - override def transform(tree: Tree)(implicit c: Context): Tree = { - /* A possibly polymorphic apply to be considered for tail call transformation. */ - def rewriteApply(tree: Tree, sym: Symbol, required: Boolean = false): Tree = { - def receiverArgumentsAndSymbol(t: Tree, accArgs: List[List[Tree]] = Nil, accT: List[Tree] = Nil): - (Tree, Tree, List[List[Tree]], List[Tree], Symbol) = t match { - case TypeApply(fun, targs) if fun.symbol eq t.symbol => receiverArgumentsAndSymbol(fun, accArgs, targs) - case Apply(fn, args) if fn.symbol == t.symbol => receiverArgumentsAndSymbol(fn, args :: accArgs, accT) - case Select(qual, _) => (qual, t, accArgs, accT, t.symbol) - case x: This => (x, x, accArgs, accT, x.symbol) - case x: Ident if x.symbol eq method => (EmptyTree, x, accArgs, accT, x.symbol) - case x => (x, x, accArgs, accT, x.symbol) - } - - val (prefix, call, arguments, typeArguments, symbol) = receiverArgumentsAndSymbol(tree) - val hasConformingTargs = (typeArguments zip methTparams).forall{x => x._1.tpe <:< x._2.tpe} - val recv = noTailTransform(prefix) - - val targs = typeArguments.map(noTailTransform) - val argumentss = arguments.map(noTailTransforms) - - val recvWiden = recv.tpe.widenDealias - - val receiverIsSame = enclosingClass.typeRef.widenDealias =:= recvWiden - val receiverIsSuper = (method.name eq sym) && enclosingClass.typeRef.widen <:< recvWiden - val receiverIsThis = recv.tpe =:= thisType || recv.tpe.widen =:= thisType - - val isRecursiveCall = (method eq sym) - - def continue = { - val method = noTailTransform(call) - val methodWithTargs = if (targs.nonEmpty) TypeApply(method, targs) else method - if (methodWithTargs.tpe.widen.isParameterless) methodWithTargs - else argumentss.foldLeft(methodWithTargs) { - // case (method, args) => Apply(method, args) // Dotty deviation no auto-detupling yet. Interesting that one can do it in Scala2! - (method, args) => Apply(method, args) - } - } - def fail(reason: String) = { - if (isMandatory || required) c.error(s"Cannot rewrite recursive call: $reason", tree.pos) - else c.debuglog("Cannot rewrite recursive call at: " + tree.pos + " because: " + reason) - continue - } - - def rewriteTailCall(recv: Tree): Tree = { - c.debuglog("Rewriting tail recursive call: " + tree.pos) - rewrote = true - val receiver = noTailTransform(recv) - - val callTargs: List[tpd.Tree] = - if (abstractOverClass) { - val classTypeArgs = recv.tpe.baseTypeWithArgs(enclosingClass).argInfos - targs ::: classTypeArgs.map(x => ref(x.typeSymbol)) - } else targs - - val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef) - val thisPassed = - if (this.method.owner.isClass) - method.appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head)) - else method - - val res = - if (thisPassed.tpe.widen.isParameterless) thisPassed - else argumentss.foldLeft(thisPassed) { - (met, ar) => Apply(met, ar) // Dotty deviation no auto-detupling yet. - } - res - } - - if (isRecursiveCall) { - if (ctx.tailPos) { - if (!hasConformingTargs) fail("it changes type arguments on a polymorphic recursive call") - else if (recv eq EmptyTree) rewriteTailCall(This(enclosingClass.asClass)) - else if (receiverIsSame || receiverIsThis) rewriteTailCall(recv) - else fail("it changes type of 'this' on a polymorphic recursive call") - } - else fail(defaultReason) - } else { - if (receiverIsSuper) fail("it contains a recursive call targeting a supertype") - else continue - } - } - - def rewriteTry(tree: Try): Try = { - if (tree.finalizer eq EmptyTree) { - // SI-1672 Catches are in tail position when there is no finalizer - tpd.cpy.Try(tree)( - noTailTransform(tree.expr), - transformSub(tree.cases), - EmptyTree - ) - } - else { - tpd.cpy.Try(tree)( - noTailTransform(tree.expr), - noTailTransforms(tree.cases), - noTailTransform(tree.finalizer) - ) - } - } - - val res: Tree = tree match { - - case Ident(qual) => - val sym = tree.symbol - if (sym == method && ctx.tailPos) rewriteApply(tree, sym) - else tree - - case tree: Select => - val sym = tree.symbol - if (sym == method && ctx.tailPos) rewriteApply(tree, sym) - else tpd.cpy.Select(tree)(noTailTransform(tree.qualifier), tree.name) - - case Apply(fun, args) => - val meth = fun.symbol - if (meth == defn.Boolean_|| || meth == defn.Boolean_&&) - tpd.cpy.Apply(tree)(fun, transform(args)) - else - rewriteApply(tree, meth) - - case tree@Block(stats, expr) => - tpd.cpy.Block(tree)( - noTailTransforms(stats), - transform(expr) - ) - case tree @ Typed(t: Apply, tpt) if tpt.tpe.hasAnnotation(defn.TailrecAnnot) => - tpd.Typed(rewriteApply(t, t.fun.symbol, required = true), tpt) - case tree@If(cond, thenp, elsep) => - tpd.cpy.If(tree)( - noTailTransform(cond), - transform(thenp), - transform(elsep) - ) - - case tree@CaseDef(_, _, body) => - cpy.CaseDef(tree)(body = transform(body)) - - case tree@Match(selector, cases) => - tpd.cpy.Match(tree)( - noTailTransform(selector), - transformSub(cases) - ) - - case tree: Try => - rewriteTry(tree) - - case Alternative(_) | Bind(_, _) => - assert(false, "We should never have gotten inside a pattern") - tree - - case t @ DefDef(_, _, _, _, _) => - t // todo: could improve to handle DefDef's with a label flag calls to which are in tail position - - case ValDef(_, _, _) | EmptyTree | Super(_, _) | This(_) | - Literal(_) | TypeTree() | TypeDef(_, _) => - tree - - case Return(expr, from) => - tpd.cpy.Return(tree)(noTailTransform(expr), from) - - case _ => - super.transform(tree) - } - - res - } - } - - /** If references to original `target` from fully parameterized method `derived` should be - * rewired to some fully parameterized method, that method symbol, - * otherwise NoSymbol. - */ - override protected def rewiredTarget(target: Symbol, derived: Symbol)(implicit ctx: Context): Symbol = NoSymbol -} - -object TailRec { - - final class TailContext(val tailPos: Boolean) extends AnyVal - - final val noTailContext = new TailContext(false) - final val yesTailContext = new TailContext(true) -} diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala deleted file mode 100644 index 4a09d2fef..000000000 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ /dev/null @@ -1,452 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.Names.Name -import core.DenotTransformers._ -import core.Denotations._ -import core.SymDenotations._ -import core.Contexts._ -import core.Symbols._ -import core.Types._ -import core.Flags._ -import core.Constants._ -import core.StdNames._ -import core.Decorators._ -import core.TypeErasure.isErasedType -import core.Phases.Phase -import core.Mode -import typer._ -import typer.ErrorReporting._ -import reporting.ThrowingReporter -import ast.Trees._ -import ast.{tpd, untpd} -import util.SourcePosition -import collection.mutable -import ProtoTypes._ -import config.Printers -import java.lang.AssertionError - -import dotty.tools.dotc.core.Names - -import scala.util.control.NonFatal - -/** Run by -Ycheck option after a given phase, this class retypes all syntax trees - * and verifies that the type of each tree node so obtained conforms to the type found in the tree node. - * It also performs the following checks: - * - * - The owner of each definition is the same as the owner of the current typing context. - * - Ident nodes do not refer to a denotation that would need a select to be accessible - * (see tpd.needsSelect). - * - After typer, identifiers and select nodes refer to terms only (all types should be - * represented as TypeTrees then). - */ -class TreeChecker extends Phase with SymTransformer { - import ast.tpd._ - - - private val seenClasses = collection.mutable.HashMap[String, Symbol]() - private val seenModuleVals = collection.mutable.HashMap[String, Symbol]() - - def isValidJVMName(name: Name) = - !name.exists(c => c == '.' || c == ';' || c =='[' || c == '/') - - def isValidJVMMethodName(name: Name) = - !name.exists(c => c == '.' || c == ';' || c =='[' || c == '/' || c == '<' || c == '>') - - def printError(str: String)(implicit ctx: Context) = { - ctx.echo(Console.RED + "[error] " + Console.WHITE + str) - } - - val NoSuperClass = Trait | Package - - def testDuplicate(sym: Symbol, registry: mutable.Map[String, Symbol], typ: String)(implicit ctx: Context) = { - val name = sym.fullName.toString - if (this.flatClasses && registry.contains(name)) - printError(s"$typ defined twice $sym ${sym.id} ${registry(name).id}") - registry(name) = sym - } - - def checkCompanion(symd: SymDenotation)(implicit ctx: Context): Unit = { - val cur = symd.linkedClass - val prev = ctx.atPhase(ctx.phase.prev) { implicit ctx => - symd.symbol.linkedClass - } - - if (prev.exists) - assert(cur.exists, i"companion disappeared from $symd") - } - - def transformSym(symd: SymDenotation)(implicit ctx: Context): SymDenotation = { - val sym = symd.symbol - - if (sym.isClass && !sym.isAbsent) { - val validSuperclass = sym.isPrimitiveValueClass || defn.syntheticCoreClasses.contains(sym) || - (sym eq defn.ObjectClass) || (sym is NoSuperClass) || (sym.asClass.superClass.exists) - if (!validSuperclass) - printError(s"$sym has no superclass set") - - testDuplicate(sym, seenClasses, "class") - } - - if (sym.is(Method) && sym.is(Deferred) && sym.is(Private)) - assert(false, s"$sym is both Deferred and Private") - - checkCompanion(symd) - - symd - } - - def phaseName: String = "Ycheck" - - def run(implicit ctx: Context): Unit = { - check(ctx.allPhases, ctx) - } - - private def previousPhases(phases: List[Phase])(implicit ctx: Context): List[Phase] = phases match { - case (phase: TreeTransformer) :: phases1 => - val subPhases = phase.miniPhases - val previousSubPhases = previousPhases(subPhases.toList) - if (previousSubPhases.length == subPhases.length) previousSubPhases ::: previousPhases(phases1) - else previousSubPhases - case phase :: phases1 if phase ne ctx.phase => - phase :: previousPhases(phases1) - case _ => - Nil - } - - def check(phasesToRun: Seq[Phase], ctx: Context) = { - val prevPhase = ctx.phase.prev // can be a mini-phase - val squahsedPhase = ctx.squashed(prevPhase) - ctx.echo(s"checking ${ctx.compilationUnit} after phase ${squahsedPhase}") - - val checkingCtx = ctx - .fresh - .setMode(Mode.ImplicitsEnabled) - .setReporter(new ThrowingReporter(ctx.reporter)) - - val checker = new Checker(previousPhases(phasesToRun.toList)(ctx)) - try checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) - catch { - case NonFatal(ex) => //TODO CHECK. Check that we are bootstrapped - implicit val ctx: Context = checkingCtx - println(i"*** error while checking ${ctx.compilationUnit} after phase ${checkingCtx.phase.prev} ***") - throw ex - } - } - - class Checker(phasesToCheck: Seq[Phase]) extends ReTyper { - - val nowDefinedSyms = new mutable.HashSet[Symbol] - val everDefinedSyms = new mutable.HashMap[Symbol, Tree] - - def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match { - case tree: DefTree => - val sym = tree.symbol - assert(isValidJVMName(sym.name), s"${sym.fullName} name is invalid on jvm") - everDefinedSyms.get(sym) match { - case Some(t) => - if (t ne tree) - ctx.warning(i"symbol ${sym.fullName} is defined at least twice in different parts of AST") - // should become an error - case None => - everDefinedSyms(sym) = tree - } - assert(!nowDefinedSyms.contains(sym), i"doubly defined symbol: ${sym.fullName} in $tree") - - if (ctx.settings.YcheckMods.value) { - tree match { - case t: MemberDef => - if (t.name ne sym.name) ctx.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") - // todo: compare trees inside annotations - case _ => - } - } - - nowDefinedSyms += tree.symbol - //ctx.echo(i"defined: ${tree.symbol}") - val res = op - nowDefinedSyms -= tree.symbol - //ctx.echo(i"undefined: ${tree.symbol}") - res - case _ => op - } - - def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) = - trees.foldRightBN(op)(withDefinedSym(_)(_)) - - def withDefinedSymss[T](vparamss: List[List[untpd.ValDef]])(op: => T)(implicit ctx: Context): T = - vparamss.foldRightBN(op)(withDefinedSyms(_)(_)) - - def assertDefined(tree: untpd.Tree)(implicit ctx: Context) = - if (tree.symbol.maybeOwner.isTerm) - assert(nowDefinedSyms contains tree.symbol, i"undefined symbol ${tree.symbol}") - - /** assert Java classes are not used as objects */ - def assertIdentNotJavaClass(tree: Tree)(implicit ctx: Context): Unit = tree match { - case _ : untpd.Ident => - assert(!tree.symbol.is(JavaModule), "Java class can't be used as value: " + tree) - case _ => - } - - /** check Java classes are not used as objects */ - def checkIdentNotJavaClass(tree: Tree)(implicit ctx: Context): Unit = tree match { - // case tree: untpd.Ident => - // case tree: untpd.Select => - // case tree: untpd.Bind => - case vd : ValDef => - assertIdentNotJavaClass(vd.forceIfLazy) - case dd : DefDef => - assertIdentNotJavaClass(dd.forceIfLazy) - // case tree: untpd.TypeDef => - case Apply(fun, args) => - assertIdentNotJavaClass(fun) - args.foreach(assertIdentNotJavaClass _) - // case tree: untpd.This => - // case tree: untpd.Literal => - // case tree: untpd.New => - case Typed(expr, _) => - assertIdentNotJavaClass(expr) - case NamedArg(_, arg) => - assertIdentNotJavaClass(arg) - case Assign(_, rhs) => - assertIdentNotJavaClass(rhs) - case Block(stats, expr) => - stats.foreach(assertIdentNotJavaClass _) - assertIdentNotJavaClass(expr) - case If(_, thenp, elsep) => - assertIdentNotJavaClass(thenp) - assertIdentNotJavaClass(elsep) - // case tree: untpd.Closure => - case Match(selector, cases) => - assertIdentNotJavaClass(selector) - cases.foreach(caseDef => assertIdentNotJavaClass(caseDef.body)) - case Return(expr, _) => - assertIdentNotJavaClass(expr) - case Try(expr, cases, finalizer) => - assertIdentNotJavaClass(expr) - cases.foreach(caseDef => assertIdentNotJavaClass(caseDef.body)) - assertIdentNotJavaClass(finalizer) - // case tree: TypeApply => - // case tree: Super => - case SeqLiteral(elems, _) => - elems.foreach(assertIdentNotJavaClass) - // case tree: TypeTree => - // case tree: SingletonTypeTree => - // case tree: AndTypeTree => - // case tree: OrTypeTree => - // case tree: RefinedTypeTree => - // case tree: AppliedTypeTree => - // case tree: ByNameTypeTree => - // case tree: TypeBoundsTree => - // case tree: Alternative => - // case tree: PackageDef => - case Annotated(arg, _) => - assertIdentNotJavaClass(arg) - case _ => - } - - override def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = { - val tpdTree = super.typed(tree, pt) - checkIdentNotJavaClass(tpdTree) - tpdTree - } - - override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context): tpd.Tree = { - val res = tree match { - case _: untpd.UnApply => - // can't recheck patterns - tree.asInstanceOf[tpd.Tree] - case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[_] => - super.typedUnadapted(tree) - case _ if tree.isType => - promote(tree) - case _ => - val tree1 = super.typedUnadapted(tree, pt) - def isSubType(tp1: Type, tp2: Type) = - (tp1 eq tp2) || // accept NoType / NoType - (tp1 <:< tp2) - def divergenceMsg(tp1: Type, tp2: Type) = - s"""Types differ - |Original type : ${tree.typeOpt.show} - |After checking: ${tree1.tpe.show} - |Original tree : ${tree.show} - |After checking: ${tree1.show} - |Why different : - """.stripMargin + core.TypeComparer.explained((tp1 <:< tp2)(_)) - if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted - assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt)) - tree1 - } - checkNoOrphans(res.tpe) - phasesToCheck.foreach(_.checkPostCondition(res)) - res - } - - /** Check that PolyParams and MethodParams refer to an enclosing type */ - def checkNoOrphans(tp: Type)(implicit ctx: Context) = new TypeMap() { - val definedBinders = mutable.Set[Type]() - def apply(tp: Type): Type = { - tp match { - case tp: BindingType => - definedBinders += tp - mapOver(tp) - definedBinders -= tp - case tp: ParamType => - assert(definedBinders.contains(tp.binder), s"orphan param: $tp") - case tp: TypeVar => - apply(tp.underlying) - case _ => - mapOver(tp) - } - tp - } - }.apply(tp) - - def checkNotRepeated(tree: Tree)(implicit ctx: Context): tree.type = { - def allowedRepeated = (tree.symbol.flags is Case) && tree.tpe.widen.isRepeatedParam - - assert(!tree.tpe.widen.isRepeatedParam || allowedRepeated, i"repeated parameter type not allowed here: $tree") - tree - } - - /** Check that all methods have MethodicType */ - def isMethodType(pt: Type)(implicit ctx: Context): Boolean = pt match { - case at: AnnotatedType => isMethodType(at.tpe) - case _: MethodicType => true // MethodType, ExprType, PolyType - case _ => false - } - - override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = { - assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase) - assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree # ${tree.uniqueId}") - assertDefined(tree) - - checkNotRepeated(super.typedIdent(tree, pt)) - } - - /** Makes sure the symbol in the tree can be approximately reconstructed by - * calling `member` on the qualifier type. - * Approximately means: The two symbols might be different but one still overrides the other. - */ - override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { - assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase) - val tpe = tree.typeOpt - val sym = tree.symbol - if (!tpe.isInstanceOf[WithFixedSym] && sym.exists && !sym.is(Private)) { - val qualTpe = tree.qualifier.typeOpt - val member = - if (sym.is(Private)) qualTpe.member(tree.name) - else qualTpe.nonPrivateMember(tree.name) - val memberSyms = member.alternatives.map(_.symbol) - assert(memberSyms.exists(mbr => - sym == mbr || - sym.overriddenSymbol(mbr.owner.asClass) == mbr || - mbr.overriddenSymbol(sym.owner.asClass) == sym), - ex"""symbols differ for $tree - |was : $sym - |alternatives by type: $memberSyms%, % of types ${memberSyms.map(_.info)}%, % - |qualifier type : ${tree.qualifier.typeOpt} - |tree type : ${tree.typeOpt} of class ${tree.typeOpt.getClass}""") - } - checkNotRepeated(super.typedSelect(tree, pt)) - } - - override def typedThis(tree: untpd.This)(implicit ctx: Context) = { - val res = super.typedThis(tree) - val cls = res.symbol - assert(cls.isStaticOwner || ctx.owner.isContainedIn(cls), i"error while typing $tree, ${ctx.owner} is not contained in $cls") - res - } - - private def checkOwner(tree: untpd.Tree)(implicit ctx: Context): Unit = { - def ownerMatches(symOwner: Symbol, ctxOwner: Symbol): Boolean = - symOwner == ctxOwner || - ctxOwner.isWeakOwner && ownerMatches(symOwner, ctxOwner.owner) || - ctx.phase.labelsReordered && symOwner.isWeakOwner && ownerMatches(symOwner.owner, ctxOwner) - assert(ownerMatches(tree.symbol.owner, ctx.owner), - i"bad owner; ${tree.symbol} has owner ${tree.symbol.owner}, expected was ${ctx.owner}\n" + - i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %") - } - - override def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = { - val TypeDef(_, impl @ Template(constr, _, _, _)) = cdef - assert(cdef.symbol == cls) - assert(impl.symbol.owner == cls) - assert(constr.symbol.owner == cls) - assert(cls.primaryConstructor == constr.symbol, i"mismatch, primary constructor ${cls.primaryConstructor}, in tree = ${constr.symbol}") - checkOwner(impl) - checkOwner(impl.constr) - - def isNonMagicalMethod(x: Symbol) = - x.is(Method) && - !x.isCompanionMethod && - !x.isValueClassConvertMethod - - val symbolsNotDefined = cls.classInfo.decls.toSet.filter(isNonMagicalMethod) -- impl.body.map(_.symbol) - constr.symbol - - assert(symbolsNotDefined.isEmpty, - i" $cls tree does not define methods: ${symbolsNotDefined.toList}%, %\n" + - i"expected: ${cls.classInfo.decls.toSet.filter(isNonMagicalMethod).toList}%, %\n" + - i"defined: ${impl.body.map(_.symbol)}%, %") - - super.typedClassDef(cdef, cls) - } - - override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = - withDefinedSyms(ddef.tparams) { - withDefinedSymss(ddef.vparamss) { - if (!sym.isClassConstructor && !(sym.name eq Names.STATIC_CONSTRUCTOR)) assert(isValidJVMMethodName(sym.name), s"${sym.fullName} name is invalid on jvm") - val tpdTree = super.typedDefDef(ddef, sym) - assert(isMethodType(sym.info), i"wrong type, expect a method type for ${sym.fullName}, but found: ${sym.info}") - tpdTree - } - } - - override def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = { - withDefinedSyms(tree.pat.asInstanceOf[tpd.Tree].filterSubTrees(_.isInstanceOf[ast.Trees.Bind[_]])) { - super.typedCase(tree, pt, selType, gadtSyms) - } - } - - override def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = - withDefinedSyms(tree.stats) { super.typedBlock(tree, pt) } - - override def typedInlined(tree: untpd.Inlined, pt: Type)(implicit ctx: Context) = - withDefinedSyms(tree.bindings) { super.typedInlined(tree, pt) } - - /** Check that all defined symbols have legal owners. - * An owner is legal if it is either the same as the context's owner - * or there's an owner chain of valdefs starting at the context's owner and - * reaching up to the symbol's owner. The reason for this relaxed matching - * is that we should be able to pull out an expression as an initializer - * of a helper value without having to do a change owner traversal of the expression. - */ - override def typedStats(trees: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { - for (tree <- trees) tree match { - case tree: untpd.DefTree => checkOwner(tree) - case _: untpd.Thicket => assert(false, i"unexpanded thicket $tree in statement sequence $trees%\n%") - case _ => - } - super.typedStats(trees, exprOwner) - } - - override def ensureNoLocalRefs(tree: Tree, pt: Type, localSyms: => List[Symbol], forcedDefined: Boolean = false)(implicit ctx: Context): Tree = - tree - - override def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = { - def isPrimaryConstructorReturn = - ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass) - if (ctx.mode.isExpr && - !tree.isEmpty && - !isPrimaryConstructorReturn && - !pt.isInstanceOf[FunProto]) - assert(tree.tpe <:< pt, { - val mismatch = err.typeMismatchMsg(tree.tpe, pt) - i"""|${mismatch.msg} - |tree = $tree""".stripMargin - }) - tree - } - } -} diff --git a/src/dotty/tools/dotc/transform/TreeExtractors.scala b/src/dotty/tools/dotc/transform/TreeExtractors.scala deleted file mode 100644 index 7a5c5df9d..000000000 --- a/src/dotty/tools/dotc/transform/TreeExtractors.scala +++ /dev/null @@ -1,48 +0,0 @@ -package dotty.tools.dotc -package transform - -import ast.{Trees, tpd} -import core._, core.Decorators._ -import Contexts._, Flags._, Trees._, Types._, StdNames._, Symbols._ -import ValueClasses._ - -object TreeExtractors { - import tpd._ - - /** Match arg1.op(arg2) and extract (arg1, op.symbol, arg2) */ - object BinaryOp { - def unapply(t: Tree)(implicit ctx: Context): Option[(Tree, Symbol, Tree)] = t match { - case Apply(sel @ Select(arg1, _), List(arg2)) => - Some((arg1, sel.symbol, arg2)) - case _ => - None - } - } - - /** Match new C(args) and extract (C, args) */ - object NewWithArgs { - def unapply(t: Tree)(implicit ctx: Context): Option[(Type, List[Tree])] = t match { - case Apply(Select(New(_), nme.CONSTRUCTOR), args) => - Some((t.tpe, args)) - case _ => - None - } - } - - /** For an instance v of a value class like: - * class V(val underlying: X) extends AnyVal - * Match v.underlying() and extract v - */ - object ValueClassUnbox { - def unapply(t: Tree)(implicit ctx: Context): Option[Tree] = t match { - case Apply(sel @ Select(ref, _), Nil) => - val d = ref.tpe.widenDealias.typeSymbol.denot - if (isDerivedValueClass(d) && (sel.symbol eq valueClassUnbox(d.asClass))) { - Some(ref) - } else - None - case _ => - None - } - } -} diff --git a/src/dotty/tools/dotc/transform/TreeGen.scala b/src/dotty/tools/dotc/transform/TreeGen.scala deleted file mode 100644 index 7e507d905..000000000 --- a/src/dotty/tools/dotc/transform/TreeGen.scala +++ /dev/null @@ -1,26 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Symbols._, Contexts._, Types._, Names._, StdNames._ -import ast._ -import Trees._ -import TypeUtils._ - -object TreeGen { - - import tpd._ - - def wrapArrayMethodName(elemtp: Type)(implicit ctx: Context): TermName = { - val elemCls = elemtp.classSymbol - if (elemCls.isPrimitiveValueClass) nme.wrapXArray(elemCls.name) - else if (elemCls.derivesFrom(defn.ObjectClass) && !elemCls.isPhantomClass) nme.wrapRefArray - else nme.genericWrapArray - } - - def wrapArray(tree: Tree, elemtp: Type)(implicit ctx: Context): Tree = - ref(defn.ScalaPredefModule) - .select(wrapArrayMethodName(elemtp)) - .appliedToTypes(if (elemtp.isPrimitiveValueType) Nil else elemtp :: Nil) - .appliedTo(tree) -} diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala deleted file mode 100644 index 5385ca720..000000000 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ /dev/null @@ -1,1221 +0,0 @@ -package dotty.tools -package dotc -package transform - -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Annotations.ConcreteAnnotation -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.DenotTransformers.{InfoTransformer, DenotTransformer} -import dotty.tools.dotc.core.Denotations.SingleDenotation -import dotty.tools.dotc.core.Phases.Phase -import dotty.tools.dotc.core.SymDenotations.SymDenotation -import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.core.Flags.PackageVal -import dotty.tools.dotc.core.Mode -import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.core.Decorators._ -import dotty.tools.dotc.util.DotClass -import scala.annotation.tailrec -import config.Printers.transforms -import scala.util.control.NonFatal - -object TreeTransforms { - import tpd._ - - /** The base class of tree transforms. For each kind of tree K, there are - * two methods which can be overridden: - * - * prepareForK // return a new TreeTransform which gets applied to the K - * // node and its children - * transformK // transform node of type K - * - * If a transform does not need to visit a node or any of its children, it - * signals this fact by returning a NoTransform from a prepare method. - * - * If all transforms in a group are NoTransforms, the tree is no longer traversed. - * - * - * Performance analysis: Taking the dotty compiler frontend as a use case, we are aiming for a warm performance of - * about 4000 lines / sec. This means 6 seconds for a codebase of 24'000 lines. Of these the frontend consumes - * over 2.5 seconds, erasure and code generation will most likely consume over 1 second each. So we would have - * about 1 sec for all other transformations in our budget. Of this second, let's assume a maximum of 20% for - * the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with - * 0.2sec, or roughly 600M processor cycles. - * - * Now, to the amount of work that needs to be done. The codebase produces an average of about 250'000 trees after typechecking. - * Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100 - * micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison, - * scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits - * of a node by a transformation. Each visit has a budget of 20 processor cycles. - * - * A more detailed breakdown: I assume that about one third of all transformations have real work to do for each node. - * This might look high, but keep in mind that the most common nodes are Idents and Selects, and most transformations - * touch these. By contrast the amount of work for generating new transformations should be negligible. - * - * So, in 400 clock cycles we need to (1) perform a pattern match according to the type of node, (2) generate new - * transformations if applicable, (3) reconstitute the tree node from the result of transforming the children, and - * (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable - * for achieving this goal, but there can be no wasted cycles anywhere. - */ - abstract class TreeTransform extends DotClass { - - def phase: MiniPhase - - def treeTransformPhase: Phase = phase.next - - def prepareForIdent(tree: Ident)(implicit ctx: Context) = this - def prepareForSelect(tree: Select)(implicit ctx: Context) = this - def prepareForThis(tree: This)(implicit ctx: Context) = this - def prepareForSuper(tree: Super)(implicit ctx: Context) = this - def prepareForApply(tree: Apply)(implicit ctx: Context) = this - def prepareForTypeApply(tree: TypeApply)(implicit ctx: Context) = this - def prepareForLiteral(tree: Literal)(implicit ctx: Context) = this - def prepareForNew(tree: New)(implicit ctx: Context) = this - def prepareForTyped(tree: Typed)(implicit ctx: Context) = this - def prepareForAssign(tree: Assign)(implicit ctx: Context) = this - def prepareForBlock(tree: Block)(implicit ctx: Context) = this - def prepareForIf(tree: If)(implicit ctx: Context) = this - def prepareForClosure(tree: Closure)(implicit ctx: Context) = this - def prepareForMatch(tree: Match)(implicit ctx: Context) = this - def prepareForCaseDef(tree: CaseDef)(implicit ctx: Context) = this - def prepareForReturn(tree: Return)(implicit ctx: Context) = this - def prepareForTry(tree: Try)(implicit ctx: Context) = this - def prepareForSeqLiteral(tree: SeqLiteral)(implicit ctx: Context) = this - def prepareForInlined(tree: Inlined)(implicit ctx: Context) = this - def prepareForTypeTree(tree: TypeTree)(implicit ctx: Context) = this - def prepareForBind(tree: Bind)(implicit ctx: Context) = this - def prepareForAlternative(tree: Alternative)(implicit ctx: Context) = this - def prepareForTypeDef(tree: TypeDef)(implicit ctx: Context) = this - def prepareForUnApply(tree: UnApply)(implicit ctx: Context) = this - def prepareForValDef(tree: ValDef)(implicit ctx: Context) = this - def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = this - def prepareForTemplate(tree: Template)(implicit ctx: Context) = this - def prepareForPackageDef(tree: PackageDef)(implicit ctx: Context) = this - def prepareForStats(trees: List[Tree])(implicit ctx: Context) = this - - def prepareForUnit(tree: Tree)(implicit ctx: Context) = this - - def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformThis(tree: This)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformSuper(tree: Super)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformLiteral(tree: Literal)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformTyped(tree: Typed)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformMatch(tree: Match)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformCaseDef(tree: CaseDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformReturn(tree: Return)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformTry(tree: Try)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformInlined(tree: Inlined)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformBind(tree: Bind)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformAlternative(tree: Alternative)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformUnApply(tree: UnApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformPackageDef(tree: PackageDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees - def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = tree - - def transformUnit(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = tree - - /** Transform tree using all transforms of current group (including this one) */ - def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, 0) - - /** Transform subtree using all transforms following the current one in this group */ - def transformFollowingDeep(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, phase.idx + 1) - - /** Transform single node using all transforms following the current one in this group */ - def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transformSingle(tree, phase.idx + 1) - - def atGroupEnd[T](action : Context => T)(implicit ctx: Context, info: TransformerInfo) = { - val last = info.transformers(info.transformers.length - 1) - action(ctx.withPhase(last.phase.next)) - } - } - - /** A phase that defines a TreeTransform to be used in a group */ - trait MiniPhase extends Phase { thisPhase => - def treeTransform: TreeTransform - - /** id of this mini phase in group */ - var idx: Int = _ - - /** List of names of phases that should have finished their processing of all compilation units - * before this phase starts - */ - def runsAfterGroupsOf: Set[Class[_ <: Phase]] = Set.empty - - protected def mkTreeTransformer = new TreeTransformer { - override def phaseName: String = thisPhase.phaseName - override def miniPhases = Array(thisPhase) - } - - override def run(implicit ctx: Context): Unit = { - mkTreeTransformer.run - } - } - - /** A mini phase that is its own tree transform */ - abstract class MiniPhaseTransform extends TreeTransform with MiniPhase { - def treeTransform = this - def phase = this - } - - /** A helper trait to transform annotations on MemberDefs */ - trait AnnotationTransformer extends MiniPhaseTransform with DenotTransformer { - - val annotationTransformer = mkTreeTransformer - override final def treeTransformPhase = this - // need to run at own phase because otherwise we get ahead of ourselves in transforming denotations - - abstract override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = - super.transform(ref) match { - case ref1: SymDenotation if ref1.symbol.isDefinedInCurrentRun => - val annotTrees = ref1.annotations.map(_.tree) - val annotTrees1 = annotTrees.mapConserve(annotationTransformer.macroTransform) - if (annotTrees eq annotTrees1) ref1 - else ref1.copySymDenotation(annotations = annotTrees1.map(new ConcreteAnnotation(_))) - case ref1 => - ref1 - } - } - - @sharable val NoTransform = new TreeTransform { - def phase = unsupported("phase") - } - - type Mutator[T] = (TreeTransform, T, Context) => TreeTransform - - class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group: TreeTransformer) - - /** This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX - * Thanks to Martin for this idea - * @see NXTransformations.index for format of plan - */ - class NXTransformations { - - private def hasRedefinedMethod(cls: Class[_], name: String): Boolean = - if (cls.getDeclaredMethods.exists(_.getName == name)) cls != classOf[TreeTransform] - else hasRedefinedMethod(cls.getSuperclass, name) - - /** Create an index array `next` of size one larger than the size of `transforms` such that - * for each index i, `next(i)` is the smallest index j such that - * - * i <= j - * j == transforms.length || transform(j) defines a non-default method with given `name` - */ - private def index(transformations: Array[Class[_]], name: String): Array[Int] = { - val len = transformations.length - val next = new Array[Int](len + 1) - var nextTransform: Int = len - - /* loop invariant: nextTransform == the smallest j such that - * i < j and - * j == transforms.length || transform(j) defines a non-default method with given `name` - */ - next(len) = len - var i = len - 1 - while (i >= 0) { - // update nextTransform if this phase redefines the method - if (hasRedefinedMethod(transformations(i), name)) { - nextTransform = i - } - next(i) = nextTransform - i -= 1 - } - next - } - - private def indexUpdate(prev: Array[Int], changedTransformation: Class[_], index: Int, name: String, copy: Boolean = true) = { - val isDefinedNow = hasRedefinedMethod(changedTransformation, name) - val wasDefinedBefore = prev(index) == index - if (isDefinedNow == wasDefinedBefore) prev - else { - val result = if (copy) prev.clone() else prev - val oldValue = result(index) - val newValue = - if (wasDefinedBefore /* && !isDefinedNow */ ) prev(index + 1) - else index // isDefinedNow - var i = index - while (i >= 0 && result(i) == oldValue) { - result(i) = newValue - i -= 1 - } - result - } - } - - def this(transformations: Array[Class[_]]) = { - this() - nxPrepIdent = index(transformations, "prepareForIdent") - nxPrepSelect = index(transformations, "prepareForSelect") - nxPrepThis = index(transformations, "prepareForThis") - nxPrepSuper = index(transformations, "prepareForSuper") - nxPrepApply = index(transformations, "prepareForApply") - nxPrepTypeApply = index(transformations, "prepareForTypeApply") - nxPrepLiteral = index(transformations, "prepareForLiteral") - nxPrepNew = index(transformations, "prepareForNew") - nxPrepTyped = index(transformations, "prepareForTyped") - nxPrepAssign = index(transformations, "prepareForAssign") - nxPrepBlock = index(transformations, "prepareForBlock") - nxPrepIf = index(transformations, "prepareForIf") - nxPrepClosure = index(transformations, "prepareForClosure") - nxPrepCaseDef = index(transformations, "prepareForCaseDef") - nxPrepMatch = index(transformations, "prepareForMatch") - nxPrepReturn = index(transformations, "prepareForReturn") - nxPrepTry = index(transformations, "prepareForTry") - nxPrepSeqLiteral = index(transformations, "prepareForSeqLiteral") - nxPrepInlined = index(transformations, "prepareForInlined") - nxPrepTypeTree = index(transformations, "prepareForTypeTree") - nxPrepBind = index(transformations, "prepareForBind") - nxPrepAlternative = index(transformations, "prepareForAlternative") - nxPrepUnApply = index(transformations, "prepareForUnApply") - nxPrepValDef = index(transformations, "prepareForValDef") - nxPrepDefDef = index(transformations, "prepareForDefDef") - nxPrepTypeDef = index(transformations, "prepareForTypeDef") - nxPrepTemplate = index(transformations, "prepareForTemplate") - nxPrepPackageDef = index(transformations, "prepareForPackageDef") - nxPrepStats = index(transformations, "prepareForStats") - nxPrepUnit = index(transformations, "prepareForUnit") - - nxTransIdent = index(transformations, "transformIdent") - nxTransSelect = index(transformations, "transformSelect") - nxTransThis = index(transformations, "transformThis") - nxTransSuper = index(transformations, "transformSuper") - nxTransApply = index(transformations, "transformApply") - nxTransTypeApply = index(transformations, "transformTypeApply") - nxTransLiteral = index(transformations, "transformLiteral") - nxTransNew = index(transformations, "transformNew") - nxTransTyped = index(transformations, "transformTyped") - nxTransAssign = index(transformations, "transformAssign") - nxTransBlock = index(transformations, "transformBlock") - nxTransIf = index(transformations, "transformIf") - nxTransClosure = index(transformations, "transformClosure") - nxTransMatch = index(transformations, "transformMatch") - nxTransCaseDef = index(transformations, "transformCaseDef") - nxTransReturn = index(transformations, "transformReturn") - nxTransTry = index(transformations, "transformTry") - nxTransSeqLiteral = index(transformations, "transformSeqLiteral") - nxTransInlined = index(transformations, "transformInlined") - nxTransTypeTree = index(transformations, "transformTypeTree") - nxTransBind = index(transformations, "transformBind") - nxTransAlternative = index(transformations, "transformAlternative") - nxTransUnApply = index(transformations, "transformUnApply") - nxTransValDef = index(transformations, "transformValDef") - nxTransDefDef = index(transformations, "transformDefDef") - nxTransTypeDef = index(transformations, "transformTypeDef") - nxTransTemplate = index(transformations, "transformTemplate") - nxTransPackageDef = index(transformations, "transformPackageDef") - nxTransStats = index(transformations, "transformStats") - nxTransUnit = index(transformations, "transformUnit") - nxTransOther = index(transformations, "transformOther") - } - - def this(transformations: Array[TreeTransform]) = { - this(transformations.map(_.getClass).asInstanceOf[Array[Class[_]]]) - } - - def this(prev: NXTransformations, changedTransformation: TreeTransform, transformationIndex: Int, reuse: Boolean = false) = { - this() - val copy = !reuse - val changedTransformationClass = changedTransformation.getClass - nxPrepIdent = indexUpdate(prev.nxPrepIdent, changedTransformationClass, transformationIndex, "prepareForIdent", copy) - nxPrepSelect = indexUpdate(prev.nxPrepSelect, changedTransformationClass, transformationIndex, "prepareForSelect", copy) - nxPrepThis = indexUpdate(prev.nxPrepThis, changedTransformationClass, transformationIndex, "prepareForThis", copy) - nxPrepSuper = indexUpdate(prev.nxPrepSuper, changedTransformationClass, transformationIndex, "prepareForSuper", copy) - nxPrepApply = indexUpdate(prev.nxPrepApply, changedTransformationClass, transformationIndex, "prepareForApply", copy) - nxPrepTypeApply = indexUpdate(prev.nxPrepTypeApply, changedTransformationClass, transformationIndex, "prepareForTypeApply", copy) - nxPrepLiteral = indexUpdate(prev.nxPrepLiteral, changedTransformationClass, transformationIndex, "prepareForLiteral", copy) - nxPrepNew = indexUpdate(prev.nxPrepNew, changedTransformationClass, transformationIndex, "prepareForNew", copy) - nxPrepTyped = indexUpdate(prev.nxPrepTyped, changedTransformationClass, transformationIndex, "prepareForTyped", copy) - nxPrepAssign = indexUpdate(prev.nxPrepAssign, changedTransformationClass, transformationIndex, "prepareForAssign", copy) - nxPrepBlock = indexUpdate(prev.nxPrepBlock, changedTransformationClass, transformationIndex, "prepareForBlock", copy) - nxPrepIf = indexUpdate(prev.nxPrepIf, changedTransformationClass, transformationIndex, "prepareForIf", copy) - nxPrepClosure = indexUpdate(prev.nxPrepClosure, changedTransformationClass, transformationIndex, "prepareForClosure", copy) - nxPrepMatch = indexUpdate(prev.nxPrepMatch, changedTransformationClass, transformationIndex, "prepareForMatch", copy) - nxPrepCaseDef = indexUpdate(prev.nxPrepCaseDef, changedTransformationClass, transformationIndex, "prepareForCaseDef", copy) - nxPrepReturn = indexUpdate(prev.nxPrepReturn, changedTransformationClass, transformationIndex, "prepareForReturn", copy) - nxPrepTry = indexUpdate(prev.nxPrepTry, changedTransformationClass, transformationIndex, "prepareForTry", copy) - nxPrepSeqLiteral = indexUpdate(prev.nxPrepSeqLiteral, changedTransformationClass, transformationIndex, "prepareForSeqLiteral", copy) - nxPrepInlined = indexUpdate(prev.nxPrepInlined, changedTransformationClass, transformationIndex, "prepareForInlined", copy) - nxPrepTypeTree = indexUpdate(prev.nxPrepTypeTree, changedTransformationClass, transformationIndex, "prepareForTypeTree", copy) - nxPrepBind = indexUpdate(prev.nxPrepBind, changedTransformationClass, transformationIndex, "prepareForBind", copy) - nxPrepAlternative = indexUpdate(prev.nxPrepAlternative, changedTransformationClass, transformationIndex, "prepareForAlternative", copy) - nxPrepUnApply = indexUpdate(prev.nxPrepUnApply, changedTransformationClass, transformationIndex, "prepareForUnApply", copy) - nxPrepValDef = indexUpdate(prev.nxPrepValDef, changedTransformationClass, transformationIndex, "prepareForValDef", copy) - nxPrepDefDef = indexUpdate(prev.nxPrepDefDef, changedTransformationClass, transformationIndex, "prepareForDefDef", copy) - nxPrepTypeDef = indexUpdate(prev.nxPrepTypeDef, changedTransformationClass, transformationIndex, "prepareForTypeDef", copy) - nxPrepTemplate = indexUpdate(prev.nxPrepTemplate, changedTransformationClass, transformationIndex, "prepareForTemplate", copy) - nxPrepPackageDef = indexUpdate(prev.nxPrepPackageDef, changedTransformationClass, transformationIndex, "prepareForPackageDef", copy) - nxPrepStats = indexUpdate(prev.nxPrepStats, changedTransformationClass, transformationIndex, "prepareForStats", copy) - - nxTransIdent = indexUpdate(prev.nxTransIdent, changedTransformationClass, transformationIndex, "transformIdent", copy) - nxTransSelect = indexUpdate(prev.nxTransSelect, changedTransformationClass, transformationIndex, "transformSelect", copy) - nxTransThis = indexUpdate(prev.nxTransThis, changedTransformationClass, transformationIndex, "transformThis", copy) - nxTransSuper = indexUpdate(prev.nxTransSuper, changedTransformationClass, transformationIndex, "transformSuper", copy) - nxTransApply = indexUpdate(prev.nxTransApply, changedTransformationClass, transformationIndex, "transformApply", copy) - nxTransTypeApply = indexUpdate(prev.nxTransTypeApply, changedTransformationClass, transformationIndex, "transformTypeApply", copy) - nxTransLiteral = indexUpdate(prev.nxTransLiteral, changedTransformationClass, transformationIndex, "transformLiteral", copy) - nxTransNew = indexUpdate(prev.nxTransNew, changedTransformationClass, transformationIndex, "transformNew", copy) - nxTransTyped = indexUpdate(prev.nxTransTyped, changedTransformationClass, transformationIndex, "transformTyped", copy) - nxTransAssign = indexUpdate(prev.nxTransAssign, changedTransformationClass, transformationIndex, "transformAssign", copy) - nxTransBlock = indexUpdate(prev.nxTransBlock, changedTransformationClass, transformationIndex, "transformBlock", copy) - nxTransIf = indexUpdate(prev.nxTransIf, changedTransformationClass, transformationIndex, "transformIf", copy) - nxTransClosure = indexUpdate(prev.nxTransClosure, changedTransformationClass, transformationIndex, "transformClosure", copy) - nxTransMatch = indexUpdate(prev.nxTransMatch, changedTransformationClass, transformationIndex, "transformMatch", copy) - nxTransCaseDef = indexUpdate(prev.nxTransCaseDef, changedTransformationClass, transformationIndex, "transformCaseDef", copy) - nxTransReturn = indexUpdate(prev.nxTransReturn, changedTransformationClass, transformationIndex, "transformReturn", copy) - nxTransTry = indexUpdate(prev.nxTransTry, changedTransformationClass, transformationIndex, "transformTry", copy) - nxTransSeqLiteral = indexUpdate(prev.nxTransSeqLiteral, changedTransformationClass, transformationIndex, "transformSeqLiteral", copy) - nxTransInlined = indexUpdate(prev.nxTransInlined, changedTransformationClass, transformationIndex, "transformInlined", copy) - nxTransTypeTree = indexUpdate(prev.nxTransTypeTree, changedTransformationClass, transformationIndex, "transformTypeTree", copy) - nxTransBind = indexUpdate(prev.nxTransBind, changedTransformationClass, transformationIndex, "transformBind", copy) - nxTransAlternative = indexUpdate(prev.nxTransAlternative, changedTransformationClass, transformationIndex, "transformAlternative", copy) - nxTransUnApply = indexUpdate(prev.nxTransUnApply, changedTransformationClass, transformationIndex, "transformUnApply", copy) - nxTransValDef = indexUpdate(prev.nxTransValDef, changedTransformationClass, transformationIndex, "transformValDef", copy) - nxTransDefDef = indexUpdate(prev.nxTransDefDef, changedTransformationClass, transformationIndex, "transformDefDef", copy) - nxTransTypeDef = indexUpdate(prev.nxTransTypeDef, changedTransformationClass, transformationIndex, "transformTypeDef", copy) - nxTransTemplate = indexUpdate(prev.nxTransTemplate, changedTransformationClass, transformationIndex, "transformTemplate", copy) - nxTransPackageDef = indexUpdate(prev.nxTransPackageDef, changedTransformationClass, transformationIndex, "transformPackageDef", copy) - nxTransStats = indexUpdate(prev.nxTransStats, changedTransformationClass, transformationIndex, "transformStats", copy) - nxTransOther = indexUpdate(prev.nxTransOther, changedTransformationClass, transformationIndex, "transformOther", copy) - } - - /** Those arrays are used as "execution plan" in order to only execute non-trivial transformations\preparations - * for every integer i array(i) contains first non trivial transformation\preparation on particular tree subtype. - * If no nontrivial transformation are left stored value is greater than transformers.size - */ - var nxPrepIdent: Array[Int] = _ - var nxPrepSelect: Array[Int] = _ - var nxPrepThis: Array[Int] = _ - var nxPrepSuper: Array[Int] = _ - var nxPrepApply: Array[Int] = _ - var nxPrepTypeApply: Array[Int] = _ - var nxPrepLiteral: Array[Int] = _ - var nxPrepNew: Array[Int] = _ - var nxPrepTyped: Array[Int] = _ - var nxPrepAssign: Array[Int] = _ - var nxPrepBlock: Array[Int] = _ - var nxPrepIf: Array[Int] = _ - var nxPrepClosure: Array[Int] = _ - var nxPrepMatch: Array[Int] = _ - var nxPrepCaseDef: Array[Int] = _ - var nxPrepReturn: Array[Int] = _ - var nxPrepTry: Array[Int] = _ - var nxPrepSeqLiteral: Array[Int] = _ - var nxPrepInlined: Array[Int] = _ - var nxPrepTypeTree: Array[Int] = _ - var nxPrepBind: Array[Int] = _ - var nxPrepAlternative: Array[Int] = _ - var nxPrepUnApply: Array[Int] = _ - var nxPrepValDef: Array[Int] = _ - var nxPrepDefDef: Array[Int] = _ - var nxPrepTypeDef: Array[Int] = _ - var nxPrepTemplate: Array[Int] = _ - var nxPrepPackageDef: Array[Int] = _ - var nxPrepStats: Array[Int] = _ - var nxPrepUnit: Array[Int] = _ - - var nxTransIdent: Array[Int] = _ - var nxTransSelect: Array[Int] = _ - var nxTransThis: Array[Int] = _ - var nxTransSuper: Array[Int] = _ - var nxTransApply: Array[Int] = _ - var nxTransTypeApply: Array[Int] = _ - var nxTransLiteral: Array[Int] = _ - var nxTransNew: Array[Int] = _ - var nxTransTyped: Array[Int] = _ - var nxTransAssign: Array[Int] = _ - var nxTransBlock: Array[Int] = _ - var nxTransIf: Array[Int] = _ - var nxTransClosure: Array[Int] = _ - var nxTransMatch: Array[Int] = _ - var nxTransCaseDef: Array[Int] = _ - var nxTransReturn: Array[Int] = _ - var nxTransTry: Array[Int] = _ - var nxTransSeqLiteral: Array[Int] = _ - var nxTransInlined: Array[Int] = _ - var nxTransTypeTree: Array[Int] = _ - var nxTransBind: Array[Int] = _ - var nxTransAlternative: Array[Int] = _ - var nxTransUnApply: Array[Int] = _ - var nxTransValDef: Array[Int] = _ - var nxTransDefDef: Array[Int] = _ - var nxTransTypeDef: Array[Int] = _ - var nxTransTemplate: Array[Int] = _ - var nxTransPackageDef: Array[Int] = _ - var nxTransStats: Array[Int] = _ - var nxTransUnit: Array[Int] = _ - var nxTransOther: Array[Int] = _ - } - - /** A group of tree transforms that are applied in sequence during the same phase */ - abstract class TreeTransformer extends Phase { - - def miniPhases: Array[MiniPhase] - - override def run(implicit ctx: Context): Unit = { - val curTree = ctx.compilationUnit.tpdTree - val newTree = macroTransform(curTree) - ctx.compilationUnit.tpdTree = newTree - } - - def mutateTransformers[T](info: TransformerInfo, mutator: Mutator[T], mutationPlan: Array[Int], tree: T, cur: Int)(implicit ctx: Context) = { - var transformersCopied = false - var nxCopied = false - var result = info.transformers - var resultNX = info.nx - var i = mutationPlan(cur) - // @DarkDimius You commented on the previous version - // - // var i = mutationPlan(0) // if TreeTransform.transform() method didn't exist we could have used mutationPlan(cur) - // - // But we need to use `cur` or otherwise we call prepare actions preceding the - // phase that issued a transformFollowing. This can lead to "denotation not defined - // here" errors. Note that tests still pass with the current modified code. - val l = result.length - var allDone = i < l - while (i < l) { - val oldTransform = result(i) - val newTransform = mutator(oldTransform, tree, ctx.withPhase(oldTransform.treeTransformPhase)) - allDone = allDone && (newTransform eq NoTransform) - if (!(oldTransform eq newTransform)) { - if (!transformersCopied) result = result.clone() - transformersCopied = true - result(i) = newTransform - if (!(newTransform.getClass == oldTransform.getClass)) { - resultNX = new NXTransformations(resultNX, newTransform, i, nxCopied) - nxCopied = true - } - } - i = mutationPlan(i + 1) - } - if (allDone) null - else if (!transformersCopied) info - else new TransformerInfo(result, resultNX, info.group) - } - - val prepForIdent: Mutator[Ident] = (trans, tree, ctx) => trans.prepareForIdent(tree)(ctx) - val prepForSelect: Mutator[Select] = (trans, tree, ctx) => trans.prepareForSelect(tree)(ctx) - val prepForThis: Mutator[This] = (trans, tree, ctx) => trans.prepareForThis(tree)(ctx) - val prepForSuper: Mutator[Super] = (trans, tree, ctx) => trans.prepareForSuper(tree)(ctx) - val prepForApply: Mutator[Apply] = (trans, tree, ctx) => trans.prepareForApply(tree)(ctx) - val prepForTypeApply: Mutator[TypeApply] = (trans, tree, ctx) => trans.prepareForTypeApply(tree)(ctx) - val prepForNew: Mutator[New] = (trans, tree, ctx) => trans.prepareForNew(tree)(ctx) - val prepForTyped: Mutator[Typed] = (trans, tree, ctx) => trans.prepareForTyped(tree)(ctx) - val prepForAssign: Mutator[Assign] = (trans, tree, ctx) => trans.prepareForAssign(tree)(ctx) - val prepForLiteral: Mutator[Literal] = (trans, tree, ctx) => trans.prepareForLiteral(tree)(ctx) - val prepForBlock: Mutator[Block] = (trans, tree, ctx) => trans.prepareForBlock(tree)(ctx) - val prepForIf: Mutator[If] = (trans, tree, ctx) => trans.prepareForIf(tree)(ctx) - val prepForClosure: Mutator[Closure] = (trans, tree, ctx) => trans.prepareForClosure(tree)(ctx) - val prepForMatch: Mutator[Match] = (trans, tree, ctx) => trans.prepareForMatch(tree)(ctx) - val prepForCaseDef: Mutator[CaseDef] = (trans, tree, ctx) => trans.prepareForCaseDef(tree)(ctx) - val prepForReturn: Mutator[Return] = (trans, tree, ctx) => trans.prepareForReturn(tree)(ctx) - val prepForTry: Mutator[Try] = (trans, tree, ctx) => trans.prepareForTry(tree)(ctx) - val prepForSeqLiteral: Mutator[SeqLiteral] = (trans, tree, ctx) => trans.prepareForSeqLiteral(tree)(ctx) - val prepForInlined: Mutator[Inlined] = (trans, tree, ctx) => trans.prepareForInlined(tree)(ctx) - val prepForTypeTree: Mutator[TypeTree] = (trans, tree, ctx) => trans.prepareForTypeTree(tree)(ctx) - val prepForBind: Mutator[Bind] = (trans, tree, ctx) => trans.prepareForBind(tree)(ctx) - val prepForAlternative: Mutator[Alternative] = (trans, tree, ctx) => trans.prepareForAlternative(tree)(ctx) - val prepForUnApply: Mutator[UnApply] = (trans, tree, ctx) => trans.prepareForUnApply(tree)(ctx) - val prepForValDef: Mutator[ValDef] = (trans, tree, ctx) => trans.prepareForValDef(tree)(ctx) - val prepForDefDef: Mutator[DefDef] = (trans, tree, ctx) => trans.prepareForDefDef(tree)(ctx) - val prepForTypeDef: Mutator[TypeDef] = (trans, tree, ctx) => trans.prepareForTypeDef(tree)(ctx) - val prepForTemplate: Mutator[Template] = (trans, tree, ctx) => trans.prepareForTemplate(tree)(ctx) - val prepForPackageDef: Mutator[PackageDef] = (trans, tree, ctx) => trans.prepareForPackageDef(tree)(ctx) - val prepForStats: Mutator[List[Tree]] = (trans, trees, ctx) => trans.prepareForStats(trees)(ctx) - val prepForUnit: Mutator[Tree] = (trans, tree, ctx) => trans.prepareForUnit(tree)(ctx) - - val initialTransformationsCache = miniPhases.zipWithIndex.map { - case (miniPhase, id) => - miniPhase.idx = id - miniPhase.treeTransform - } - - val initialInfoCache = new TransformerInfo(initialTransformationsCache, new NXTransformations(initialTransformationsCache), this) - - def macroTransform(t: Tree)(implicit ctx: Context): Tree = { - val info = initialInfoCache - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForUnit, info.nx.nxPrepUnit, t, 0) - if (mutatedInfo eq null) t - else goUnit(transform(t, mutatedInfo, 0), mutatedInfo.nx.nxTransUnit(0)) - } - - @tailrec - final private[TreeTransforms] def goIdent(tree: Ident, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformIdent(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Ident => goIdent(t, info.nx.nxTransIdent(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goSelect(tree: Select, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformSelect(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Select => goSelect(t, info.nx.nxTransSelect(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goThis(tree: This, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformThis(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: This => goThis(t, info.nx.nxTransThis(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goSuper(tree: Super, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformSuper(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Super => goSuper(t, info.nx.nxTransSuper(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goApply(tree: Apply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Apply => goApply(t, info.nx.nxTransApply(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goTypeApply(tree: TypeApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformTypeApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: TypeApply => goTypeApply(t, info.nx.nxTransTypeApply(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goNew(tree: New, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformNew(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: New => goNew(t, info.nx.nxTransNew(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goTyped(tree: Typed, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformTyped(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Typed => goTyped(t, info.nx.nxTransTyped(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goAssign(tree: Assign, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformAssign(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Assign => goAssign(t, info.nx.nxTransAssign(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goLiteral(tree: Literal, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformLiteral(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Literal => goLiteral(t, info.nx.nxTransLiteral(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goBlock(tree: Block, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformBlock(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Block => goBlock(t, info.nx.nxTransBlock(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goIf(tree: If, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformIf(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: If => goIf(t, info.nx.nxTransIf(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goClosure(tree: Closure, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformClosure(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Closure => goClosure(t, info.nx.nxTransClosure(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goMatch(tree: Match, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformMatch(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Match => goMatch(t, info.nx.nxTransMatch(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goCaseDef(tree: CaseDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformCaseDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: CaseDef => goCaseDef(t, info.nx.nxTransCaseDef(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goReturn(tree: Return, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformReturn(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Return => goReturn(t, info.nx.nxTransReturn(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goTry(tree: Try, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformTry(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Try => goTry(t, info.nx.nxTransTry(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goSeqLiteral(tree: SeqLiteral, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformSeqLiteral(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: SeqLiteral => goSeqLiteral(t, info.nx.nxTransSeqLiteral(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goInlined(tree: Inlined, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformInlined(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Inlined => goInlined(t, info.nx.nxTransInlined(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goTypeTree(tree: TypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformTypeTree(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: TypeTree => goTypeTree(t, info.nx.nxTransTypeTree(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goBind(tree: Bind, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformBind(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Bind => goBind(t, info.nx.nxTransBind(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goAlternative(tree: Alternative, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformAlternative(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Alternative => goAlternative(t, info.nx.nxTransAlternative(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goValDef(tree: ValDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformValDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: ValDef => goValDef(t, info.nx.nxTransValDef(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goDefDef(tree: DefDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformDefDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: DefDef => goDefDef(t, info.nx.nxTransDefDef(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goUnApply(tree: UnApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformUnApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: UnApply => goUnApply(t, info.nx.nxTransUnApply(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goTypeDef(tree: TypeDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformTypeDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: TypeDef => goTypeDef(t, info.nx.nxTransTypeDef(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goTemplate(tree: Template, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformTemplate(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: Template => goTemplate(t, info.nx.nxTransTemplate(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goPackageDef(tree: PackageDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - trans.transformPackageDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { - case t: PackageDef => goPackageDef(t, info.nx.nxTransPackageDef(cur + 1)) - case t => transformSingle(t, cur + 1) - } - } else tree - } - - @tailrec - final private[TreeTransforms] def goUnit(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - val t = trans.transformUnit(tree)(ctx.withPhase(trans.treeTransformPhase), info) - goUnit(t, info.nx.nxTransUnit(cur + 1)) - } else tree - } - - final private[TreeTransforms] def goOther(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - val t = trans.transformOther(tree)(ctx.withPhase(trans.treeTransformPhase), info) - transformSingle(t, cur + 1) - } else tree - } - - final private[TreeTransforms] def goNamed(tree: NameTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = - tree match { - case tree: Ident => goIdent(tree, info.nx.nxTransIdent(cur)) - case tree: Select => goSelect(tree, info.nx.nxTransSelect(cur)) - case tree: Bind => goBind(tree, cur) - case tree: ValDef if !tree.isEmpty => goValDef(tree, info.nx.nxTransValDef(cur)) - case tree: DefDef => goDefDef(tree, info.nx.nxTransDefDef(cur)) - case tree: TypeDef => goTypeDef(tree, info.nx.nxTransTypeDef(cur)) - case _ => tree - } - - final private[TreeTransforms] def goUnnamed(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = - tree match { - case tree: This => goThis(tree, info.nx.nxTransThis(cur)) - case tree: Super => goSuper(tree, info.nx.nxTransSuper(cur)) - case tree: Apply => goApply(tree, info.nx.nxTransApply(cur)) - case tree: TypeApply => goTypeApply(tree, info.nx.nxTransTypeApply(cur)) - case tree: Literal => goLiteral(tree, info.nx.nxTransLiteral(cur)) - case tree: New => goNew(tree, info.nx.nxTransNew(cur)) - case tree: Typed => goTyped(tree, info.nx.nxTransTyped(cur)) - case tree: Assign => goAssign(tree, info.nx.nxTransAssign(cur)) - case tree: Block => goBlock(tree, info.nx.nxTransBlock(cur)) - case tree: If => goIf(tree, info.nx.nxTransIf(cur)) - case tree: Closure => goClosure(tree, info.nx.nxTransClosure(cur)) - case tree: Match => goMatch(tree, info.nx.nxTransMatch(cur)) - case tree: CaseDef => goCaseDef(tree, info.nx.nxTransCaseDef(cur)) - case tree: Return => goReturn(tree, info.nx.nxTransReturn(cur)) - case tree: Try => goTry(tree, info.nx.nxTransTry(cur)) - case tree: SeqLiteral => goSeqLiteral(tree, info.nx.nxTransSeqLiteral(cur)) - case tree: Inlined => goInlined(tree, info.nx.nxTransInlined(cur)) - case tree: TypeTree => goTypeTree(tree, info.nx.nxTransTypeTree(cur)) - case tree: Alternative => goAlternative(tree, info.nx.nxTransAlternative(cur)) - case tree: UnApply => goUnApply(tree, info.nx.nxTransUnApply(cur)) - case tree: Template => goTemplate(tree, info.nx.nxTransTemplate(cur)) - case tree: PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur)) - case Thicket(trees) => tree - case tree => goOther(tree, info.nx.nxTransOther(cur)) - } - - final private[TreeTransforms] def transformSingle(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = - if (cur < info.transformers.length) { - tree match { - // split one big match into 2 smaller ones - case tree: NameTree => goNamed(tree, cur) - case tree => goUnnamed(tree, cur) - } - } else tree - - // TODO merge with localCtx in MacroTransform - // Generally: If we will keep MacroTransform, merge common behavior with TreeTransform - def localContext(sym: Symbol)(implicit ctx: Context) = { - val owner = if (sym is PackageVal) sym.moduleClass else sym - ctx.fresh.setOwner(owner) - } - - final private[TreeTransforms] def transformNamed(tree: NameTree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = - tree match { - case tree: Ident => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForIdent, info.nx.nxPrepIdent, tree, cur) - // Dotty deviation: implicits need explicit type - if (mutatedInfo eq null) tree - else goIdent(tree, mutatedInfo.nx.nxTransIdent(cur)) - case tree: Select => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSelect, info.nx.nxPrepSelect, tree, cur) - if (mutatedInfo eq null) tree - else { - val qual = transform(tree.qualifier, mutatedInfo, cur) - goSelect(cpy.Select(tree)(qual, tree.name), mutatedInfo.nx.nxTransSelect(cur)) - } - case tree: Bind => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBind, info.nx.nxPrepBind, tree, cur) - if (mutatedInfo eq null) tree - else { - val body = transform(tree.body, mutatedInfo, cur) - goBind(cpy.Bind(tree)(tree.name, body), mutatedInfo.nx.nxTransBind(cur)) - } - case tree: ValDef if !tree.isEmpty => // As a result of discussing with Martin: emptyValDefs shouldn't be copied // NAME - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForValDef, info.nx.nxPrepValDef, tree, cur) - if (mutatedInfo eq null) tree - else { - val nestedCtx = if (tree.symbol.exists) localContext(tree.symbol) else ctx - val tpt = transform(tree.tpt, mutatedInfo, cur)(nestedCtx) - val rhs = transform(tree.rhs, mutatedInfo, cur)(nestedCtx) - goValDef(cpy.ValDef(tree)(tree.name, tpt, rhs), mutatedInfo.nx.nxTransValDef(cur)) - } - case tree: DefDef => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForDefDef, info.nx.nxPrepDefDef, tree, cur) - if (mutatedInfo eq null) tree - else { - val nestedCtx = localContext(tree.symbol) - val tparams = transformSubTrees(tree.tparams, mutatedInfo, cur)(nestedCtx) - val vparams = tree.vparamss.mapConserve(x => transformSubTrees(x, mutatedInfo, cur)(nestedCtx)) - val tpt = transform(tree.tpt, mutatedInfo, cur)(nestedCtx) - val rhs = transform(tree.rhs, mutatedInfo, cur)(nestedCtx) - goDefDef(cpy.DefDef(tree)(tree.name, tparams, vparams, tpt, rhs), mutatedInfo.nx.nxTransDefDef(cur)) - } - case tree: TypeDef => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeDef, info.nx.nxPrepTypeDef, tree, cur) - if (mutatedInfo eq null) tree - else { - val rhs = transform(tree.rhs, mutatedInfo, cur)(localContext(tree.symbol)) - goTypeDef(cpy.TypeDef(tree)(tree.name, rhs), mutatedInfo.nx.nxTransTypeDef(cur)) - } - case _ => - tree - } - - final private[TreeTransforms] def transformUnnamed(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = - tree match { - case tree: This => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForThis, info.nx.nxPrepThis, tree, cur) - if (mutatedInfo eq null) tree - else goThis(tree, mutatedInfo.nx.nxTransThis(cur)) - case tree: Super => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSuper, info.nx.nxPrepSuper, tree, cur) - if (mutatedInfo eq null) tree - else { - val qual = transform(tree.qual, mutatedInfo, cur) - goSuper(cpy.Super(tree)(qual, tree.mix), mutatedInfo.nx.nxTransSuper(cur)) - } - case tree: Apply => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForApply, info.nx.nxPrepApply, tree, cur) - if (mutatedInfo eq null) tree - else { - val fun = transform(tree.fun, mutatedInfo, cur) - val args = transformSubTrees(tree.args, mutatedInfo, cur) - goApply(cpy.Apply(tree)(fun, args), mutatedInfo.nx.nxTransApply(cur)) - } - case tree: TypeApply => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeApply, info.nx.nxPrepTypeApply, tree, cur) - if (mutatedInfo eq null) tree - else { - val fun = transform(tree.fun, mutatedInfo, cur) - val args = transformTrees(tree.args, mutatedInfo, cur) - goTypeApply(cpy.TypeApply(tree)(fun, args), mutatedInfo.nx.nxTransTypeApply(cur)) - } - case tree: Literal => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForLiteral, info.nx.nxPrepLiteral, tree, cur) - if (mutatedInfo eq null) tree - else goLiteral(tree, mutatedInfo.nx.nxTransLiteral(cur)) - case tree: New => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForNew, info.nx.nxPrepNew, tree, cur) - if (mutatedInfo eq null) tree - else { - val tpt = transform(tree.tpt, mutatedInfo, cur) - goNew(cpy.New(tree)(tpt), mutatedInfo.nx.nxTransNew(cur)) - } - case tree: Typed => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTyped, info.nx.nxPrepTyped, tree, cur) - if (mutatedInfo eq null) tree - else { - val expr = transform(tree.expr, mutatedInfo, cur) - val tpt = transform(tree.tpt, mutatedInfo, cur) - goTyped(cpy.Typed(tree)(expr, tpt), mutatedInfo.nx.nxTransTyped(cur)) - } - case tree: Assign => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAssign, info.nx.nxPrepAssign, tree, cur) - if (mutatedInfo eq null) tree - else { - val lhs = transform(tree.lhs, mutatedInfo, cur) - val rhs = transform(tree.rhs, mutatedInfo, cur) - goAssign(cpy.Assign(tree)(lhs, rhs), mutatedInfo.nx.nxTransAssign(cur)) - } - case tree: Block => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBlock, info.nx.nxPrepBlock, tree, cur) - if (mutatedInfo eq null) tree - else { - val stats = transformStats(tree.stats, ctx.owner, mutatedInfo, cur) - val expr = transform(tree.expr, mutatedInfo, cur) - goBlock(cpy.Block(tree)(stats, expr), mutatedInfo.nx.nxTransBlock(cur)) - } - case tree: If => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForIf, info.nx.nxPrepIf, tree, cur) - if (mutatedInfo eq null) tree - else { - val cond = transform(tree.cond, mutatedInfo, cur) - val thenp = transform(tree.thenp, mutatedInfo, cur) - val elsep = transform(tree.elsep, mutatedInfo, cur) - goIf(cpy.If(tree)(cond, thenp, elsep), mutatedInfo.nx.nxTransIf(cur)) - } - case tree: Closure => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForClosure, info.nx.nxPrepClosure, tree, cur) - if (mutatedInfo eq null) tree - else { - val env = transformTrees(tree.env, mutatedInfo, cur) - val meth = transform(tree.meth, mutatedInfo, cur) - val tpt = transform(tree.tpt, mutatedInfo, cur) - goClosure(cpy.Closure(tree)(env, meth, tpt), mutatedInfo.nx.nxTransClosure(cur)) - } - case tree: Match => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForMatch, info.nx.nxPrepMatch, tree, cur) - if (mutatedInfo eq null) tree - else { - val selector = transform(tree.selector, mutatedInfo, cur) - val cases = transformSubTrees(tree.cases, mutatedInfo, cur) - goMatch(cpy.Match(tree)(selector, cases), mutatedInfo.nx.nxTransMatch(cur)) - } - case tree: CaseDef => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur) - if (mutatedInfo eq null) tree - else { - val pat = transform(tree.pat, mutatedInfo, cur)(ctx.addMode(Mode.Pattern)) - val guard = transform(tree.guard, mutatedInfo, cur) - val body = transform(tree.body, mutatedInfo, cur) - goCaseDef(cpy.CaseDef(tree)(pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur)) - } - case tree: Return => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForReturn, info.nx.nxPrepReturn, tree, cur) - if (mutatedInfo eq null) tree - else { - val expr = transform(tree.expr, mutatedInfo, cur) - val from = tree.from - // don't transform the `from` part, as this is not a normal ident, but - // a pointer to the enclosing method. Transforming this as a normal ident - // can go wrong easily. If a transformation is needed, it should be - // the responsibility of the transformReturn method to handle this also. - goReturn(cpy.Return(tree)(expr, from), mutatedInfo.nx.nxTransReturn(cur)) - } - case tree: Try => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTry, info.nx.nxPrepTry, tree, cur) - if (mutatedInfo eq null) tree - else { - val block = transform(tree.expr, mutatedInfo, cur) - val cases1 = tree.cases.mapConserve(transform(_, mutatedInfo, cur)).asInstanceOf[List[CaseDef]] - val finalizer = transform(tree.finalizer, mutatedInfo, cur) - goTry(cpy.Try(tree)(block, cases1, finalizer), mutatedInfo.nx.nxTransTry(cur)) - } - case tree: SeqLiteral => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSeqLiteral, info.nx.nxPrepSeqLiteral, tree, cur) - if (mutatedInfo eq null) tree - else { - val elems = transformTrees(tree.elems, mutatedInfo, cur) - val elemtpt = transform(tree.elemtpt, mutatedInfo, cur) - goSeqLiteral(cpy.SeqLiteral(tree)(elems, elemtpt), mutatedInfo.nx.nxTransSeqLiteral(cur)) - } - case tree: Inlined => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForInlined, info.nx.nxPrepInlined, tree, cur) - if (mutatedInfo eq null) tree - else { - val bindings = transformSubTrees(tree.bindings, mutatedInfo, cur) - val expansion = transform(tree.expansion, mutatedInfo, cur)(inlineContext(tree)) - goInlined(cpy.Inlined(tree)(tree.call, bindings, expansion), mutatedInfo.nx.nxTransInlined(cur)) - } - case tree: TypeTree => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur) - if (mutatedInfo eq null) tree - else goTypeTree(tree, mutatedInfo.nx.nxTransTypeTree(cur)) - case tree: Alternative => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAlternative, info.nx.nxPrepAlternative, tree, cur) - if (mutatedInfo eq null) tree - else { - val trees = transformTrees(tree.trees, mutatedInfo, cur) - goAlternative(cpy.Alternative(tree)(trees), mutatedInfo.nx.nxTransAlternative(cur)) - } - case tree: UnApply => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForUnApply, info.nx.nxPrepUnApply, tree, cur) - if (mutatedInfo eq null) tree - else { - val fun = transform(tree.fun, mutatedInfo, cur) - val implicits = transformTrees(tree.implicits, mutatedInfo, cur) - val patterns = transformTrees(tree.patterns, mutatedInfo, cur) - goUnApply(cpy.UnApply(tree)(fun, implicits, patterns), mutatedInfo.nx.nxTransUnApply(cur)) - } - case tree: Template => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTemplate, info.nx.nxPrepTemplate, tree, cur) - if (mutatedInfo eq null) tree - else { - val constr = transformSub(tree.constr, mutatedInfo, cur) - val parents = transformTrees(tree.parents, mutatedInfo, cur)(ctx.superCallContext) - val self = transformSub(tree.self, mutatedInfo, cur) - val body = transformStats(tree.body, tree.symbol, mutatedInfo, cur) - goTemplate(cpy.Template(tree)(constr, parents, self, body), mutatedInfo.nx.nxTransTemplate(cur)) - } - case tree: PackageDef => - implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForPackageDef, info.nx.nxPrepPackageDef, tree, cur) - if (mutatedInfo eq null) tree - else { - val nestedCtx = localContext(tree.symbol) - val pid = transformSub(tree.pid, mutatedInfo, cur) - val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx) - goPackageDef(cpy.PackageDef(tree)(pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) - } - case Thicket(trees) => - cpy.Thicket(tree)(transformTrees(trees, info, cur)) - case tree => - implicit val originalInfo: TransformerInfo = info - goOther(tree, info.nx.nxTransOther(cur)) - } - - private var crashingTree: Tree = EmptyTree - - def transform(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show} at ${ctx.phase}", transforms, show = true) { - try - if (cur < info.transformers.length) { - // if cur > 0 then some of the symbols can be created by already performed transformations - // this means that their denotations could not exists in previous period - val pctx = ctx.withPhase(info.transformers(cur).treeTransformPhase) - tree match { - //split one big match into 2 smaller ones - case tree: NameTree => transformNamed(tree, info, cur)(pctx) - case tree => transformUnnamed(tree, info, cur)(pctx) - } - } else tree - catch { - case NonFatal(ex) => - if (tree ne crashingTree) { - crashingTree = tree - println(i"exception while transforming $tree of class ${tree.getClass} # ${tree.uniqueId}") - } - throw ex - } - } - - @tailrec - final private[TreeTransforms] def goStats(trees: List[Tree], cur: Int)(implicit ctx: Context, info: TransformerInfo): List[Tree] = { - if (cur < info.transformers.length) { - val trans = info.transformers(cur) - val stats = trans.transformStats(trees)(ctx.withPhase(trans.treeTransformPhase), info) - goStats(stats, info.nx.nxTransStats(cur + 1)) - } else trees - } - - def transformStats(trees: List[Tree], exprOwner: Symbol, info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tree] = { - val newInfo = mutateTransformers(info, prepForStats, info.nx.nxPrepStats, trees, current) - def transformStat(stat: Tree): Tree = stat match { - case _: Import | _: DefTree => transform(stat, newInfo, current) - case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) - case _ => transform(stat, newInfo, current)(ctx.exprContext(stat, exprOwner)) - } - val newTrees = flatten(trees.mapconserve(transformStat)) - goStats(newTrees, newInfo.nx.nxTransStats(current))(ctx, newInfo) - } - - def transformTrees(trees: List[Tree], info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tree] = - flatten(trees mapConserve (x => transform(x, info, current))) - - def transformSub[Tr <: Tree](tree: Tr, info: TransformerInfo, current: Int)(implicit ctx: Context): Tr = - transform(tree, info, current).asInstanceOf[Tr] - - def transformSubTrees[Tr <: Tree](trees: List[Tr], info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tr] = - transformTrees(trees, info, current)(ctx).asInstanceOf[List[Tr]] - } -} diff --git a/src/dotty/tools/dotc/transform/TryCatchPatterns.scala b/src/dotty/tools/dotc/transform/TryCatchPatterns.scala deleted file mode 100644 index 9a6ecef51..000000000 --- a/src/dotty/tools/dotc/transform/TryCatchPatterns.scala +++ /dev/null @@ -1,99 +0,0 @@ -package dotty.tools.dotc -package transform - -import core.Symbols._ -import core.StdNames._ -import ast.Trees._ -import core.Types._ -import dotty.tools.dotc.core.Decorators._ -import dotty.tools.dotc.core.Flags -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} -import dotty.tools.dotc.util.Positions.Position - -/** Compiles the cases that can not be handled by primitive catch cases as a common pattern match. - * - * The following code: - * ``` - * try { <code> } - * catch { - * <tryCases> // Cases that can be handled by catch - * <patternMatchCases> // Cases starting with first one that can't be handled by catch - * } - * ``` - * will become: - * ``` - * try { <code> } - * catch { - * <tryCases> - * case e => e match { - * <patternMatchCases> - * } - * } - * ``` - * - * Cases that are not supported include: - * - Applies and unapplies - * - Idents - * - Alternatives - * - `case _: T =>` where `T` is not `Throwable` - * - */ -class TryCatchPatterns extends MiniPhaseTransform { - import dotty.tools.dotc.ast.tpd._ - - def phaseName: String = "tryCatchPatterns" - - override def runsAfter = Set(classOf[ElimRepeated]) - - override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match { - case Try(_, cases, _) => - cases.foreach { - case CaseDef(Typed(_, _), guard, _) => assert(guard.isEmpty, "Try case should not contain a guard.") - case CaseDef(Bind(_, _), guard, _) => assert(guard.isEmpty, "Try case should not contain a guard.") - case c => - assert(isDefaultCase(c), "Pattern in Try should be Bind, Typed or default case.") - } - case _ => - } - - override def transformTry(tree: Try)(implicit ctx: Context, info: TransformerInfo): Tree = { - val (tryCases, patternMatchCases) = tree.cases.span(isCatchCase) - val fallbackCase = mkFallbackPatterMatchCase(patternMatchCases, tree.pos) - cpy.Try(tree)(cases = tryCases ++ fallbackCase) - } - - /** Is this pattern node a catch-all or type-test pattern? */ - private def isCatchCase(cdef: CaseDef)(implicit ctx: Context): Boolean = cdef match { - case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) => isSimpleThrowable(tpt.tpe) - case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) => isSimpleThrowable(tpt.tpe) - case _ => isDefaultCase(cdef) - } - - private def isSimpleThrowable(tp: Type)(implicit ctx: Context): Boolean = tp match { - case tp @ TypeRef(pre, _) => - (pre == NoPrefix || pre.widen.typeSymbol.isStatic) && // Does not require outer class check - !tp.symbol.is(Flags.Trait) && // Traits not supported by JVM - tp.derivesFrom(defn.ThrowableClass) - case _ => - false - } - - private def mkFallbackPatterMatchCase(patternMatchCases: List[CaseDef], pos: Position)( - implicit ctx: Context, info: TransformerInfo): Option[CaseDef] = { - if (patternMatchCases.isEmpty) None - else { - val exName = ctx.freshName("ex").toTermName - val fallbackSelector = - ctx.newSymbol(ctx.owner, exName, Flags.Synthetic | Flags.Case, defn.ThrowableType, coord = pos) - val sel = Ident(fallbackSelector.termRef).withPos(pos) - val rethrow = CaseDef(EmptyTree, EmptyTree, Throw(ref(fallbackSelector))) - Some(CaseDef( - Bind(fallbackSelector, Underscore(fallbackSelector.info).withPos(pos)), - EmptyTree, - transformFollowing(Match(sel, patternMatchCases ::: rethrow :: Nil))) - ) - } - } - -} diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala deleted file mode 100644 index 3774127fa..000000000 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ /dev/null @@ -1,124 +0,0 @@ -package dotty.tools.dotc -package transform - -import core.Contexts._ -import core.Symbols._ -import core.Types._ -import core.Constants._ -import core.StdNames._ -import core.TypeErasure.isUnboundedGeneric -import ast.Trees._ -import Erasure.Boxing._ -import core.TypeErasure._ -import ValueClasses._ - -/** This transform normalizes type tests and type casts, - * also replacing type tests with singleton argument type with reference equality check - * Any remaining type tests - * - use the object methods $isInstanceOf and $asInstanceOf - * - have a reference type as receiver - * - can be translated directly to machine instructions - * - * - * Unfortunately this phase ended up being not Y-checkable unless types are erased. A cast to an ConstantType(3) or x.type - * cannot be rewritten before erasure. - */ -trait TypeTestsCasts { - import ast.tpd._ - - // override def phaseName: String = "typeTestsCasts" - - def interceptTypeApply(tree: TypeApply)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show}", show = true) { - tree.fun match { - case fun @ Select(qual, selector) => - val sym = tree.symbol - - def isPrimitive(tp: Type) = tp.classSymbol.isPrimitiveValueClass - - def derivedTree(qual1: Tree, sym: Symbol, tp: Type) = - cpy.TypeApply(tree)(qual1.select(sym).withPos(qual.pos), List(TypeTree(tp))) - - def qualCls = qual.tpe.widen.classSymbol - - def transformIsInstanceOf(expr:Tree, argType: Type): Tree = { - def argCls = argType.classSymbol - if ((expr.tpe <:< argType) && isPureExpr(expr)) - Literal(Constant(true)) withPos tree.pos - else if (argCls.isPrimitiveValueClass) - if (qualCls.isPrimitiveValueClass) Literal(Constant(qualCls == argCls)) withPos tree.pos - else transformIsInstanceOf(expr, defn.boxedType(argCls.typeRef)) - else argType.dealias match { - case _: SingletonType => - val cmpOp = if (argType derivesFrom defn.AnyValClass) defn.Any_equals else defn.Object_eq - expr.select(cmpOp).appliedTo(singleton(argType)) - case AndType(tp1, tp2) => - evalOnce(expr) { fun => - val erased1 = transformIsInstanceOf(fun, tp1) - val erased2 = transformIsInstanceOf(fun, tp2) - erased1 match { - case Literal(Constant(true)) => erased2 - case _ => - erased2 match { - case Literal(Constant(true)) => erased1 - case _ => erased1 and erased2 - } - } - } - case defn.MultiArrayOf(elem, ndims) if isUnboundedGeneric(elem) => - def isArrayTest(arg: Tree) = - ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal(Constant(ndims))) - if (ndims == 1) isArrayTest(qual) - else evalOnce(qual) { qual1 => - derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1) - } - case _ => - derivedTree(expr, defn.Any_isInstanceOf, argType) - } - } - - def transformAsInstanceOf(argType: Type): Tree = { - def argCls = argType.widen.classSymbol - if (qual.tpe <:< argType) - Typed(qual, tree.args.head) - else if (qualCls.isPrimitiveValueClass) { - if (argCls.isPrimitiveValueClass) primitiveConversion(qual, argCls) - else derivedTree(box(qual), defn.Any_asInstanceOf, argType) - } - else if (argCls.isPrimitiveValueClass) - unbox(qual.ensureConforms(defn.ObjectType), argType) - else if (isDerivedValueClass(argCls)) { - qual // adaptToType in Erasure will do the necessary type adaptation - } - else - derivedTree(qual, defn.Any_asInstanceOf, argType) - } - - /** Transform isInstanceOf OrType - * - * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B] - * - * The transform happens before erasure of `argType`, thus cannot be merged - * with `transformIsInstanceOf`, which depends on erased type of `argType`. - */ - def transformOrTypeTest(qual: Tree, argType: Type): Tree = argType.dealias match { - case OrType(tp1, tp2) => - evalOnce(qual) { fun => - transformOrTypeTest(fun, tp1) - .select(nme.OR) - .appliedTo(transformOrTypeTest(fun, tp2)) - } - case _ => - transformIsInstanceOf(qual, erasure(argType)) - } - - if (sym eq defn.Any_isInstanceOf) - transformOrTypeTest(qual, tree.args.head.tpe) - else if (sym eq defn.Any_asInstanceOf) - transformAsInstanceOf(erasure(tree.args.head.tpe)) - else tree - - case _ => - tree - } - } -} diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala deleted file mode 100644 index d474c77b4..000000000 --- a/src/dotty/tools/dotc/transform/TypeUtils.scala +++ /dev/null @@ -1,34 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TypeErasure.ErasedValueType -import Types._ -import Contexts._ -import Symbols._ -import Decorators._ -import StdNames.nme -import NameOps._ -import language.implicitConversions - -object TypeUtils { - implicit def decorateTypeUtils(tpe: Type): TypeUtils = new TypeUtils(tpe) -} - -/** A decorator that provides methods on types - * that are needed in the transformer pipeline. - */ -class TypeUtils(val self: Type) extends AnyVal { - import TypeUtils._ - - def isErasedValueType(implicit ctx: Context): Boolean = - self.isInstanceOf[ErasedValueType] - - def isPrimitiveValueType(implicit ctx: Context): Boolean = - self.classSymbol.isPrimitiveValueClass - - def ensureMethodic(implicit ctx: Context): Type = self match { - case self: MethodicType => self - case _ => if (ctx.erasedTypes) MethodType(Nil, self) else ExprType(self) - } -} diff --git a/src/dotty/tools/dotc/transform/VCElideAllocations.scala b/src/dotty/tools/dotc/transform/VCElideAllocations.scala deleted file mode 100644 index 1582158ac..000000000 --- a/src/dotty/tools/dotc/transform/VCElideAllocations.scala +++ /dev/null @@ -1,41 +0,0 @@ -package dotty.tools.dotc -package transform - -import ast.{Trees, tpd} -import core._, core.Decorators._ -import Contexts._, Trees._, StdNames._, Symbols._ -import DenotTransformers._, TreeTransforms._, Phases.Phase -import ExtensionMethods._, TreeExtractors._, ValueClasses._ - -/** This phase elides unnecessary value class allocations - * - * For a value class V defined as: - * class V(val underlying: U) extends AnyVal - * we avoid unnecessary allocations: - * new V(u1) == new V(u2) => u1 == u2 - * (new V(u)).underlying() => u - */ -class VCElideAllocations extends MiniPhaseTransform with IdentityDenotTransformer { - import tpd._ - - override def phaseName: String = "vcElideAllocations" - - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimErasedValueType]) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - tree match { - // new V(u1) == new V(u2) => u1 == u2 - // (We don't handle != because it has been eliminated by InterceptedMethods) - case BinaryOp(NewWithArgs(tp1, List(u1)), op, NewWithArgs(tp2, List(u2))) - if (tp1 eq tp2) && (op eq defn.Any_==) && isDerivedValueClass(tp1.typeSymbol) => - // == is overloaded in primitive classes - applyOverloaded(u1, nme.EQ, List(u2), Nil, defn.BooleanType) - - // (new V(u)).underlying() => u - case ValueClassUnbox(NewWithArgs(_, List(u))) => - u - - case _ => - tree - } -} diff --git a/src/dotty/tools/dotc/transform/VCInlineMethods.scala b/src/dotty/tools/dotc/transform/VCInlineMethods.scala deleted file mode 100644 index ddd414417..000000000 --- a/src/dotty/tools/dotc/transform/VCInlineMethods.scala +++ /dev/null @@ -1,104 +0,0 @@ -package dotty.tools.dotc -package transform - -import ast.{Trees, tpd} -import core._, core.Decorators._ -import Contexts._, Trees._, Types._ -import DenotTransformers._, TreeTransforms._, Phases.Phase -import ExtensionMethods._, ValueClasses._ - -import collection.mutable.ListBuffer - -/** This phase inlines calls to methods of value classes. - * - * A value class V after [[ExtensionMethods]] will look like: - * class V[A, B, ...](val underlying: U) extends AnyVal { - * def foo[T, S, ...](arg1: A1, arg2: A2, ...) = - * V.foo$extension[T, S, ..., A, B, ...](this)(arg1, arg2, ...) - * - * ... - * } - * - * Let e have type V, if e is a stable prefix or if V does not have any class - * type parameter, then we can rewrite: - * e.foo[X, Y, ...](args) - * as: - * V.foo$extension[X, Y, ..., e.A, e.B, ...](e)(args) - * Otherwise, we need to evaluate e first: - * { - * val ev = e - * V.foo$extension[X, Y, ..., ev.A, ev.B, ...](ev)(args) - * } - * - * This phase needs to be placed after phases which may introduce calls to - * value class methods (like [[PatternMatcher]]). This phase uses name mangling - * to find the correct extension method corresponding to a value class method - * (see [[ExtensionMethods.extensionMethod]]), therefore we choose to place it - * before phases which may perform their own name mangling on value class - * methods (like [[TypeSpecializer]]), this way [[VCInlineMethods]] does not - * need to have any knowledge of the name mangling done by other phases. - */ -class VCInlineMethods extends MiniPhaseTransform with IdentityDenotTransformer { - import tpd._ - - override def phaseName: String = "vcInlineMethods" - - override def runsAfter: Set[Class[_ <: Phase]] = - Set(classOf[ExtensionMethods], classOf[PatternMatcher]) - - /** Replace a value class method call by a call to the corresponding extension method. - * - * @param tree The tree corresponding to the method call - * @param mtArgs Type arguments for the method call not present in `tree` - * @param mArgss Arguments for the method call not present in `tree` - * @return A tree for the extension method call - */ - private def rewire(tree: Tree, mtArgs: List[Tree] = Nil, mArgss: List[List[Tree]] = Nil) - (implicit ctx: Context): Tree = - tree match { - case Apply(qual, mArgs) => - rewire(qual, mtArgs, mArgs :: mArgss) - case TypeApply(qual, mtArgs2) => - assert(mtArgs == Nil) - rewire(qual, mtArgs2, mArgss) - case sel @ Select(qual, _) => - val origMeth = sel.symbol - val ctParams = origMeth.enclosingClass.typeParams - val extensionMeth = extensionMethod(origMeth) - - if (!ctParams.isEmpty) { - evalOnce(qual) { ev => - val ctArgs = ctParams map (ev.select(_)) - ref(extensionMeth) - .appliedToTypeTrees(mtArgs ++ ctArgs) - .appliedTo(ev) - .appliedToArgss(mArgss) - } - } else { - ref(extensionMeth) - .appliedToTypeTrees(mtArgs) - .appliedTo(qual) - .appliedToArgss(mArgss) - } - } - - /** If this tree corresponds to a fully-applied value class method call, replace it - * by a call to the corresponding extension method, otherwise return it as is. - */ - private def rewireIfNeeded(tree: Tree)(implicit ctx: Context) = tree.tpe.widen match { - case tp: MethodOrPoly => - tree // The rewiring will be handled by a fully-applied parent node - case _ => - if (isMethodWithExtension(tree.symbol)) - rewire(tree).ensureConforms(tree.tpe) - else - tree - } - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = - rewireIfNeeded(tree) - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = - rewireIfNeeded(tree) - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - rewireIfNeeded(tree) -} diff --git a/src/dotty/tools/dotc/transform/ValueClasses.scala b/src/dotty/tools/dotc/transform/ValueClasses.scala deleted file mode 100644 index 93005c57a..000000000 --- a/src/dotty/tools/dotc/transform/ValueClasses.scala +++ /dev/null @@ -1,56 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import Types._ -import Symbols._ -import SymDenotations._ -import Contexts._ -import Flags._ -import StdNames._ - -/** Methods that apply to user-defined value classes */ -object ValueClasses { - - def isDerivedValueClass(d: SymDenotation)(implicit ctx: Context) = { - !ctx.settings.XnoValueClasses.value && - !d.isRefinementClass && - d.isValueClass && - (d.initial.symbol ne defn.AnyValClass) && // Compare the initial symbol because AnyVal does not exist after erasure - !d.isPrimitiveValueClass - } - - def isMethodWithExtension(d: SymDenotation)(implicit ctx: Context) = - d.isRealMethod && - isDerivedValueClass(d.owner) && - !d.isConstructor && - !d.is(SuperAccessor) && - !d.is(Macro) - - /** The member that of a derived value class that unboxes it. */ - def valueClassUnbox(d: ClassDenotation)(implicit ctx: Context): Symbol = - // (info.decl(nme.unbox)).orElse(...) uncomment once we accept unbox methods - d.classInfo.decls - .find(d => d.isTerm && d.symbol.is(ParamAccessor)) - .map(_.symbol) - .getOrElse(NoSymbol) - - /** For a value class `d`, this returns the synthetic cast from the underlying type to - * ErasedValueType defined in the companion module. This method is added to the module - * and further described in [[ExtensionMethods]]. - */ - def u2evt(d: ClassDenotation)(implicit ctx: Context): Symbol = - d.linkedClass.info.decl(nme.U2EVT).symbol - - /** For a value class `d`, this returns the synthetic cast from ErasedValueType to the - * underlying type defined in the companion module. This method is added to the module - * and further described in [[ExtensionMethods]]. - */ - def evt2u(d: ClassDenotation)(implicit ctx: Context): Symbol = - d.linkedClass.info.decl(nme.EVT2U).symbol - - /** The unboxed type that underlies a derived value class */ - def underlyingOfValueClass(d: ClassDenotation)(implicit ctx: Context): Type = - valueClassUnbox(d).info.resultType - -} diff --git a/src/dotty/tools/dotc/transform/patmat/Space.scala b/src/dotty/tools/dotc/transform/patmat/Space.scala deleted file mode 100644 index 8d926fcf0..000000000 --- a/src/dotty/tools/dotc/transform/patmat/Space.scala +++ /dev/null @@ -1,615 +0,0 @@ -package dotty.tools.dotc -package transform -package patmat - -import core.Types._ -import core.Contexts._ -import core.Flags._ -import ast.Trees._ -import ast.tpd -import core.Decorators._ -import core.Symbols._ -import core.StdNames._ -import core.NameOps._ -import core.Constants._ -import reporting.diagnostic.messages._ - -/** Space logic for checking exhaustivity and unreachability of pattern matching - * - * Space can be thought of as a set of possible values. A type or a pattern - * both refer to spaces. The space of a type is the values that inhabit the - * type. The space of a pattern is the values that can be covered by the - * pattern. - * - * Space is recursively defined as follows: - * - * 1. `Empty` is a space - * 2. For a type T, `Typ(T)` is a space - * 3. A union of spaces `S1 | S2 | ...` is a space - * 4. For a case class Kon(x1: T1, x2: T2, .., xn: Tn), if S1, S2, ..., Sn - * are spaces, then `Kon(S1, S2, ..., Sn)` is a space. - * 5. A constant `Const(value, T)` is a point in space - * 6. A stable identifier `Var(sym, T)` is a space - * - * For the problem of exhaustivity check, its formulation in terms of space is as follows: - * - * Is the space Typ(T) a subspace of the union of space covered by all the patterns? - * - * The problem of unreachable patterns can be formulated as follows: - * - * Is the space covered by a pattern a subspace of the space covered by previous patterns? - * - * Assumption: - * (1) One case class cannot be inherited directly or indirectly by another - * case class. - * (2) Inheritance of a case class cannot be well handled by the algorithm. - * - */ - - -/** space definition */ -sealed trait Space - -/** Empty space */ -case object Empty extends Space - -/** Space representing the set of all values of a type - * - * @param tp: the type this space represents - * @param decomposed: does the space result from decomposition? Used for pretty print - * - */ -case class Typ(tp: Type, decomposed: Boolean) extends Space - -/** Space representing a constructor pattern */ -case class Kon(tp: Type, params: List[Space]) extends Space - -/** Union of spaces */ -case class Or(spaces: List[Space]) extends Space - -/** Point in space */ -sealed trait Point extends Space - -/** Point representing variables(stable identifier) in patterns */ -case class Var(sym: Symbol, tp: Type) extends Point - -/** Point representing literal constants in patterns */ -case class Const(value: Constant, tp: Type) extends Point - -/** abstract space logic */ -trait SpaceLogic { - /** Is `tp1` a subtype of `tp2`? */ - def isSubType(tp1: Type, tp2: Type): Boolean - - /** Is `tp1` the same type as `tp2`? */ - def isEqualType(tp1: Type, tp2: Type): Boolean - - /** Is the type `tp` decomposable? i.e. all values of the type can be covered - * by its decomposed types. - * - * Abstract sealed class, OrType, Boolean and Java enums can be decomposed. - */ - def canDecompose(tp: Type): Boolean - - /** Return term parameter types of the case class `tp` */ - def signature(tp: Type): List[Type] - - /** Get components of decomposable types */ - def decompose(tp: Type): List[Space] - - /** Simplify space using the laws, there's no nested union after simplify */ - def simplify(space: Space): Space = space match { - case Kon(tp, spaces) => - val sp = Kon(tp, spaces.map(simplify _)) - if (sp.params.contains(Empty)) Empty - else sp - case Or(spaces) => - val set = spaces.map(simplify _).flatMap { - case Or(ss) => ss - case s => Seq(s) - } filter (_ != Empty) - - if (set.isEmpty) Empty - else if (set.size == 1) set.toList(0) - else Or(set) - case Typ(tp, _) => - if (canDecompose(tp) && decompose(tp).isEmpty) Empty - else space - case _ => space - } - - /** Flatten space to get rid of `Or` for pretty print */ - def flatten(space: Space): List[Space] = space match { - case Kon(tp, spaces) => - val flats = spaces.map(flatten _) - - flats.foldLeft(List[Kon]()) { (acc, flat) => - if (acc.isEmpty) flat.map(s => Kon(tp, Nil :+ s)) - else for (Kon(tp, ss) <- acc; s <- flat) yield Kon(tp, ss :+ s) - } - case Or(spaces) => - spaces.flatMap(flatten _) - case _ => List(space) - } - - /** Is `a` a subspace of `b`? Equivalent to `a - b == Empty`, but faster */ - def isSubspace(a: Space, b: Space): Boolean = { - def tryDecompose1(tp: Type) = canDecompose(tp) && isSubspace(Or(decompose(tp)), b) - def tryDecompose2(tp: Type) = canDecompose(tp) && isSubspace(a, Or(decompose(tp))) - - (a, b) match { - case (Empty, _) => true - case (_, Empty) => false - case (Or(ss), _) => ss.forall(isSubspace(_, b)) - case (Typ(tp1, _), Typ(tp2, _)) => - isSubType(tp1, tp2) || tryDecompose1(tp1) || tryDecompose2(tp2) - case (Typ(tp1, _), Or(ss)) => - ss.exists(isSubspace(a, _)) || tryDecompose1(tp1) - case (Typ(tp1, _), Kon(tp2, ss)) => - isSubType(tp1, tp2) && isSubspace(Kon(tp2, signature(tp2).map(Typ(_, false))), b) || - tryDecompose1(tp1) - case (Kon(tp1, ss), Typ(tp2, _)) => - isSubType(tp1, tp2) || - simplify(a) == Empty || - (isSubType(tp2, tp1) && tryDecompose1(tp1)) || - tryDecompose2(tp2) - case (Kon(_, _), Or(_)) => - simplify(minus(a, b)) == Empty - case (Kon(tp1, ss1), Kon(tp2, ss2)) => - isEqualType(tp1, tp2) && ss1.zip(ss2).forall((isSubspace _).tupled) - case (Const(v1, _), Const(v2, _)) => v1 == v2 - case (Const(_, tp1), Typ(tp2, _)) => isSubType(tp1, tp2) || tryDecompose2(tp2) - case (Const(_, _), Or(ss)) => ss.exists(isSubspace(a, _)) - case (Const(_, _), _) => false - case (_, Const(_, _)) => false - case (Var(x, _), Var(y, _)) => x == y - case (Var(_, tp1), Typ(tp2, _)) => isSubType(tp1, tp2) || tryDecompose2(tp2) - case (Var(_, _), Or(ss)) => ss.exists(isSubspace(a, _)) - case (Var(_, _), _) => false - case (_, Var(_, _)) => false - } - } - - /** Intersection of two spaces */ - def intersect(a: Space, b: Space): Space = { - def tryDecompose1(tp: Type) = intersect(Or(decompose(tp)), b) - def tryDecompose2(tp: Type) = intersect(a, Or(decompose(tp))) - - (a, b) match { - case (Empty, _) | (_, Empty) => Empty - case (_, Or(ss)) => Or(ss.map(intersect(a, _)).filterConserve(_ ne Empty)) - case (Or(ss), _) => Or(ss.map(intersect(_, b)).filterConserve(_ ne Empty)) - case (Typ(tp1, _), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) a - else if (isSubType(tp2, tp1)) b - else if (canDecompose(tp1)) tryDecompose1(tp1) - else if (canDecompose(tp2)) tryDecompose2(tp2) - else Empty - case (Typ(tp1, _), Kon(tp2, ss)) => - if (isSubType(tp2, tp1)) b - else if (isSubType(tp1, tp2)) a // problematic corner case: inheriting a case class - else if (canDecompose(tp1)) tryDecompose1(tp1) - else Empty - case (Kon(tp1, ss), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) a - else if (isSubType(tp2, tp1)) a // problematic corner case: inheriting a case class - else if (canDecompose(tp2)) tryDecompose2(tp2) - else Empty - case (Kon(tp1, ss1), Kon(tp2, ss2)) => - if (!isEqualType(tp1, tp2)) Empty - else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) Empty - else Kon(tp1, ss1.zip(ss2).map((intersect _).tupled)) - case (Const(v1, _), Const(v2, _)) => - if (v1 == v2) a else Empty - case (Const(_, tp1), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) a - else if (canDecompose(tp2)) tryDecompose2(tp2) - else Empty - case (Const(_, _), _) => Empty - case (Typ(tp1, _), Const(_, tp2)) => - if (isSubType(tp2, tp1)) b - else if (canDecompose(tp1)) tryDecompose1(tp1) - else Empty - case (_, Const(_, _)) => Empty - case (Var(x, _), Var(y, _)) => - if (x == y) a else Empty - case (Var(_, tp1), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) a - else if (canDecompose(tp2)) tryDecompose2(tp2) - else Empty - case (Var(_, _), _) => Empty - case (Typ(tp1, _), Var(_, tp2)) => - if (isSubType(tp2, tp1)) b - else if (canDecompose(tp1)) tryDecompose1(tp1) - else Empty - case (_, Var(_, _)) => Empty - } - } - - /** The space of a not covered by b */ - def minus(a: Space, b: Space): Space = { - def tryDecompose1(tp: Type) = minus(Or(decompose(tp)), b) - def tryDecompose2(tp: Type) = minus(a, Or(decompose(tp))) - - (a, b) match { - case (Empty, _) => Empty - case (_, Empty) => a - case (Typ(tp1, _), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) Empty - else if (canDecompose(tp1)) tryDecompose1(tp1) - else if (canDecompose(tp2)) tryDecompose2(tp2) - else a - case (Typ(tp1, _), Kon(tp2, ss)) => - // corner case: inheriting a case class - // rationale: every instance of `tp1` is covered by `tp2(_)` - if (isSubType(tp1, tp2)) minus(Kon(tp2, signature(tp2).map(Typ(_, false))), b) - else if (canDecompose(tp1)) tryDecompose1(tp1) - else a - case (_, Or(ss)) => - ss.foldLeft(a)(minus) - case (Or(ss), _) => - Or(ss.map(minus(_, b))) - case (Kon(tp1, ss), Typ(tp2, _)) => - // uncovered corner case: tp2 :< tp1 - if (isSubType(tp1, tp2)) Empty - else if (simplify(a) == Empty) Empty - else if (canDecompose(tp2)) tryDecompose2(tp2) - else a - case (Kon(tp1, ss1), Kon(tp2, ss2)) => - if (!isEqualType(tp1, tp2)) a - else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) a - else if (ss1.zip(ss2).forall((isSubspace _).tupled)) Empty - else - // `(_, _, _) - (Some, None, _)` becomes `(None, _, _) | (_, Some, _) | (_, _, Empty)` - Or(ss1.zip(ss2).map((minus _).tupled).zip(0 to ss2.length - 1).map { - case (ri, i) => Kon(tp1, ss1.updated(i, ri)) - }) - case (Const(v1, _), Const(v2, _)) => - if (v1 == v2) Empty else a - case (Const(_, tp1), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) Empty - else if (canDecompose(tp2)) tryDecompose2(tp2) - else a - case (Const(_, _), _) => a - case (Typ(tp1, _), Const(_, tp2)) => // Boolean & Java enum - if (canDecompose(tp1)) tryDecompose1(tp1) - else a - case (_, Const(_, _)) => a - case (Var(x, _), Var(y, _)) => - if (x == y) Empty else a - case (Var(_, tp1), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) Empty - else if (canDecompose(tp2)) tryDecompose2(tp2) - else a - case (Var(_, _), _) => a - case (_, Var(_, _)) => a - } - } -} - -/** Scala implementation of space logic */ -class SpaceEngine(implicit ctx: Context) extends SpaceLogic { - import tpd._ - - /** Return the space that represents the pattern `pat` - * - * If roundUp is true, approximate extractors to its type, - * otherwise approximate extractors to Empty - */ - def project(pat: Tree, roundUp: Boolean = true)(implicit ctx: Context): Space = pat match { - case Literal(c) => Const(c, c.tpe) - case _: BackquotedIdent => Var(pat.symbol, pat.tpe) - case Ident(_) | Select(_, _) => - pat.tpe.stripAnnots match { - case tp: TermRef => - if (pat.symbol.is(Enum)) - Const(Constant(pat.symbol), tp) - else if (tp.underlyingIterator.exists(_.classSymbol.is(Module))) - Typ(tp.widenTermRefExpr.stripAnnots, false) - else - Var(pat.symbol, tp) - case tp => Typ(tp, false) - } - case Alternative(trees) => Or(trees.map(project(_, roundUp))) - case Bind(_, pat) => project(pat) - case UnApply(_, _, pats) => - if (pat.tpe.classSymbol.is(CaseClass)) - Kon(pat.tpe.stripAnnots, pats.map(pat => project(pat, roundUp))) - else if (roundUp) Typ(pat.tpe.stripAnnots, false) - else Empty - case Typed(pat @ UnApply(_, _, _), _) => project(pat) - case Typed(expr, _) => Typ(expr.tpe.stripAnnots, true) - case _ => - Empty - } - - /* Erase a type binding according to erasure semantics in pattern matching */ - def erase(tp: Type): Type = { - def doErase(tp: Type): Type = tp match { - case tp: HKApply => erase(tp.superType) - case tp: RefinedType => erase(tp.parent) - case _ => tp - } - - tp match { - case OrType(tp1, tp2) => - OrType(erase(tp1), erase(tp2)) - case AndType(tp1, tp2) => - AndType(erase(tp1), erase(tp2)) - case _ => - val origin = doErase(tp) - if (origin =:= defn.ArrayType) tp else origin - } - } - - /** Is `tp1` a subtype of `tp2`? */ - def isSubType(tp1: Type, tp2: Type): Boolean = { - // check SI-9657 and tests/patmat/gadt.scala - erase(tp1) <:< erase(tp2) - } - - def isEqualType(tp1: Type, tp2: Type): Boolean = tp1 =:= tp2 - - /** Parameter types of the case class type `tp` */ - def signature(tp: Type): List[Type] = { - val ktor = tp.classSymbol.primaryConstructor.info - - val meth = ktor match { - case ktor: PolyType => - ktor.instantiate(tp.classSymbol.typeParams.map(_.typeRef)).asSeenFrom(tp, tp.classSymbol) - case _ => ktor - } - - // refine path-dependent type in params. refer to t9672 - meth.firstParamTypes.map(_.asSeenFrom(tp, tp.classSymbol)) - } - - /** Decompose a type into subspaces -- assume the type can be decomposed */ - def decompose(tp: Type): List[Space] = { - val children = tp.classSymbol.annotations.filter(_.symbol == ctx.definitions.ChildAnnot).map { annot => - // refer to definition of Annotation.makeChild - annot.tree match { - case Apply(TypeApply(_, List(tpTree)), _) => tpTree.symbol - } - } - - tp match { - case OrType(tp1, tp2) => List(Typ(tp1, true), Typ(tp2, true)) - case _ if tp =:= ctx.definitions.BooleanType => - List( - Const(Constant(true), ctx.definitions.BooleanType), - Const(Constant(false), ctx.definitions.BooleanType) - ) - case _ if tp.classSymbol.is(Enum) => - children.map(sym => Const(Constant(sym), tp)) - case _ => - val parts = children.map { sym => - if (sym.is(ModuleClass)) - sym.asClass.classInfo.selfType - else if (sym.info.typeParams.length > 0 || tp.isInstanceOf[TypeRef]) - refine(tp, sym.typeRef) - else - sym.typeRef - } filter { tpe => - // Child class may not always be subtype of parent: - // GADT & path-dependent types - tpe <:< expose(tp) - } - - parts.map(Typ(_, true)) - } - } - - /** Refine tp2 based on tp1 - * - * E.g. if `tp1` is `Option[Int]`, `tp2` is `Some`, then return - * `Some[Int]`. - * - * If `tp1` is `path1.A`, `tp2` is `path2.B`, and `path1` is subtype of - * `path2`, then return `path1.B`. - */ - def refine(tp1: Type, tp2: Type): Type = (tp1, tp2) match { - case (tp1: RefinedType, _) => tp1.wrapIfMember(refine(tp1.parent, tp2)) - case (tp1: HKApply, _) => refine(tp1.superType, tp2) - case (TypeRef(ref1: TypeProxy, _), tp2 @ TypeRef(ref2: TypeProxy, name)) => - if (ref1.underlying <:< ref2.underlying) TypeRef(ref1, name) else tp2 - case _ => tp2 - } - - /** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */ - def canDecompose(tp: Type): Boolean = { - tp.classSymbol.is(allOf(Abstract, Sealed)) || - tp.classSymbol.is(allOf(Trait, Sealed)) || - tp.isInstanceOf[OrType] || - tp =:= ctx.definitions.BooleanType || - tp.classSymbol.is(Enum) - } - - /** Show friendly type name with current scope in mind - * - * E.g. C.this.B --> B if current owner is C - * C.this.x.T --> x.T if current owner is C - * X[T] --> X - * C --> C if current owner is C !!! - * - */ - def showType(tp: Type): String = { - val enclosingCls = ctx.owner.enclosingClass.asClass.classInfo.symbolicTypeRef - - def isOmittable(sym: Symbol) = - sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName || - ctx.definitions.UnqualifiedOwnerTypes.exists(_.symbol == sym) || - sym.showFullName.startsWith("scala.") || - sym == enclosingCls.typeSymbol - - def refinePrefix(tp: Type): String = tp match { - case NoPrefix => "" - case tp: NamedType if isOmittable(tp.symbol) => "" - case tp: ThisType => refinePrefix(tp.tref) - case tp: RefinedType => refinePrefix(tp.parent) - case tp: NamedType => tp.name.show.stripSuffix("$") - } - - def refine(tp: Type): String = tp match { - case tp: RefinedType => refine(tp.parent) - case tp: ThisType => refine(tp.tref) - case tp: NamedType => - val pre = refinePrefix(tp.prefix) - if (tp.name == tpnme.higherKinds) pre - else if (pre.isEmpty) tp.name.show.stripSuffix("$") - else pre + "." + tp.name.show.stripSuffix("$") - case _ => tp.show.stripSuffix("$") - } - - val text = tp.stripAnnots match { - case tp: OrType => showType(tp.tp1) + " | " + showType(tp.tp2) - case tp => refine(tp) - } - - if (text.isEmpty) enclosingCls.show.stripSuffix("$") - else text - } - - /** Display spaces */ - def show(s: Space): String = { - def doShow(s: Space, mergeList: Boolean = false): String = s match { - case Empty => "" - case Const(v, _) => v.show - case Var(x, _) => x.show - case Typ(tp, decomposed) => - val sym = tp.widen.classSymbol - - if (sym.is(ModuleClass)) - showType(tp) - else if (ctx.definitions.isTupleType(tp)) - signature(tp).map(_ => "_").mkString("(", ", ", ")") - else if (sym.showFullName == "scala.collection.immutable.::") - if (mergeList) "_" else "List(_)" - else if (tp.classSymbol.is(CaseClass)) - // use constructor syntax for case class - showType(tp) + signature(tp).map(_ => "_").mkString("(", ", ", ")") - else if (signature(tp).nonEmpty) - tp.classSymbol.name + signature(tp).map(_ => "_").mkString("(", ", ", ")") - else if (decomposed) "_: " + showType(tp) - else "_" - case Kon(tp, params) => - if (ctx.definitions.isTupleType(tp)) - "(" + params.map(doShow(_)).mkString(", ") + ")" - else if (tp.widen.classSymbol.showFullName == "scala.collection.immutable.::") - if (mergeList) params.map(doShow(_, mergeList)).mkString(", ") - else params.map(doShow(_, true)).filter(_ != "Nil").mkString("List(", ", ", ")") - else - showType(tp) + params.map(doShow(_)).mkString("(", ", ", ")") - case Or(_) => - throw new Exception("incorrect flatten result " + s) - } - - flatten(s).map(doShow(_, false)).distinct.mkString(", ") - } - - def checkable(tree: Match): Boolean = { - def isCheckable(tp: Type): Boolean = tp match { - case AnnotatedType(tp, annot) => - (ctx.definitions.UncheckedAnnot != annot.symbol) && isCheckable(tp) - case _ => - // Possible to check everything, but be compatible with scalac by default - ctx.settings.YcheckAllPatmat.value || - tp.typeSymbol.is(Sealed) || - tp.isInstanceOf[OrType] || - tp.typeSymbol == ctx.definitions.BooleanType.typeSymbol || - tp.typeSymbol.is(Enum) || - canDecompose(tp) || - (defn.isTupleType(tp) && tp.dealias.argInfos.exists(isCheckable(_))) - } - - val Match(sel, cases) = tree - isCheckable(sel.tpe.widen.deAnonymize.dealiasKeepAnnots) - } - - - /** Expose refined type to eliminate reference to type variables - * - * A = B M { type T = A } ~~> M { type T = B } - * - * A <: X :> Y M { type T = A } ~~> M { type T <: X :> Y } - * - * A <: X :> Y B <: U :> V M { type T <: A :> B } ~~> M { type T <: X :> V } - * - * A = X B = Y M { type T <: A :> B } ~~> M { type T <: X :> Y } - */ - def expose(tp: Type): Type = { - def follow(tp: Type, up: Boolean): Type = tp match { - case tp: TypeProxy => - tp.underlying match { - case TypeBounds(lo, hi) => - follow(if (up) hi else lo, up) - case _ => - tp - } - case OrType(tp1, tp2) => - OrType(follow(tp1, up), follow(tp2, up)) - case AndType(tp1, tp2) => - AndType(follow(tp1, up), follow(tp2, up)) - } - - tp match { - case tp: RefinedType => - tp.refinedInfo match { - case tpa : TypeAlias => - val hi = follow(tpa.alias, true) - val lo = follow(tpa.alias, false) - val refined = if (hi =:= lo) - tpa.derivedTypeAlias(hi) - else - tpa.derivedTypeBounds(lo, hi) - - tp.derivedRefinedType( - expose(tp.parent), - tp.refinedName, - refined - ) - case tpb @ TypeBounds(lo, hi) => - tp.derivedRefinedType( - expose(tp.parent), - tp.refinedName, - tpb.derivedTypeBounds(follow(lo, false), follow(hi, true)) - ) - } - case _ => tp - } - } - - def checkExhaustivity(_match: Match): Unit = { - val Match(sel, cases) = _match - val selTyp = sel.tpe.widen.deAnonymize.dealias - - - val patternSpace = cases.map(x => project(x.pat)).reduce((a, b) => Or(List(a, b))) - val uncovered = simplify(minus(Typ(selTyp, true), patternSpace)) - - if (uncovered != Empty) - ctx.warning(PatternMatchExhaustivity(show(uncovered)), _match.pos) - } - - def checkRedundancy(_match: Match): Unit = { - val Match(sel, cases) = _match - // ignore selector type for now - // val selTyp = sel.tpe.widen.deAnonymize.dealias - - // starts from the second, the first can't be redundant - (1 until cases.length).foreach { i => - // in redundancy check, take guard as false, take extractor as match - // nothing in order to soundly approximate - val prevs = cases.take(i).map { x => - if (x.guard.isEmpty) project(x.pat, false) - else Empty - }.reduce((a, b) => Or(List(a, b))) - - val curr = project(cases(i).pat) - - if (isSubspace(curr, prevs)) { - ctx.warning(MatchCaseUnreachable(), cases(i).body.pos) - } - } - } -} diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala deleted file mode 100644 index 6c398cd72..000000000 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ /dev/null @@ -1,1351 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast.{Trees, untpd, tpd, TreeInfo} -import util.Positions._ -import util.Stats.track -import Trees.Untyped -import Mode.ImplicitsEnabled -import Contexts._ -import Flags._ -import Denotations._ -import NameOps._ -import Symbols._ -import Types._ -import Decorators._ -import ErrorReporting._ -import Trees._ -import config.Config -import Names._ -import StdNames._ -import ProtoTypes._ -import EtaExpansion._ -import Inferencing._ -import collection.mutable -import config.Printers.{typr, unapp, overload} -import TypeApplications._ -import language.implicitConversions -import reporting.diagnostic.Message - -object Applications { - import tpd._ - - def extractorMemberType(tp: Type, name: Name, errorPos: Position = NoPosition)(implicit ctx: Context) = { - val ref = tp.member(name).suchThat(_.info.isParameterless) - if (ref.isOverloaded) - errorType(i"Overloaded reference to $ref is not allowed in extractor", errorPos) - else if (ref.info.isInstanceOf[PolyType]) - errorType(i"Reference to polymorphic $ref: ${ref.info} is not allowed in extractor", errorPos) - else - ref.info.widenExpr.dealias - } - - def productSelectorTypes(tp: Type, errorPos: Position = NoPosition)(implicit ctx: Context): List[Type] = { - val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos) - sels.takeWhile(_.exists).toList - } - - def productSelectors(tp: Type)(implicit ctx: Context): List[Symbol] = { - val sels = for (n <- Iterator.from(0)) yield tp.member(nme.selectorName(n)).symbol - sels.takeWhile(_.exists).toList - } - - def getUnapplySelectors(tp: Type, args: List[untpd.Tree], pos: Position = NoPosition)(implicit ctx: Context): List[Type] = - if (args.length > 1 && !(tp.derivesFrom(defn.SeqClass))) { - val sels = productSelectorTypes(tp, pos) - if (sels.length == args.length) sels - else tp :: Nil - } else tp :: Nil - - def unapplyArgs(unapplyResult: Type, unapplyFn: Tree, args: List[untpd.Tree], pos: Position = NoPosition)(implicit ctx: Context): List[Type] = { - - def seqSelector = defn.RepeatedParamType.appliedTo(unapplyResult.elemType :: Nil) - def getTp = extractorMemberType(unapplyResult, nme.get, pos) - - // println(s"unapply $unapplyResult ${extractorMemberType(unapplyResult, nme.isDefined)}") - if (extractorMemberType(unapplyResult, nme.isDefined, pos) isRef defn.BooleanClass) { - if (getTp.exists) - if (unapplyFn.symbol.name == nme.unapplySeq) { - val seqArg = boundsToHi(getTp.elemType) - if (seqArg.exists) return args map Function.const(seqArg) - } - else return getUnapplySelectors(getTp, args, pos) - else if (defn.isProductSubType(unapplyResult)) return productSelectorTypes(unapplyResult, pos) - } - if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil - else if (unapplyResult isRef defn.BooleanClass) Nil - else { - ctx.error(i"$unapplyResult is not a valid result type of an unapply method of an extractor", pos) - Nil - } - } - - def wrapDefs(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = - if (defs != null && defs.nonEmpty) tpd.Block(defs.toList, tree) else tree -} - -import Applications._ - -trait Applications extends Compatibility { self: Typer with Dynamic => - - import Applications._ - import tpd.{ cpy => _, _ } - import untpd.cpy - import Dynamic.isDynamicMethod - - /** @tparam Arg the type of arguments, could be tpd.Tree, untpd.Tree, or Type - * @param methRef the reference to the method of the application - * @param funType the type of the function part of the application - * @param args the arguments of the application - * @param resultType the expected result type of the application - */ - abstract class Application[Arg](methRef: TermRef, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) { - - /** The type of typed arguments: either tpd.Tree or Type */ - type TypedArg - - /** Given an original argument and the type of the corresponding formal - * parameter, produce a typed argument. - */ - protected def typedArg(arg: Arg, formal: Type): TypedArg - - /** Turn a typed tree into an argument */ - protected def treeToArg(arg: Tree): Arg - - /** Check that argument corresponds to type `formal` and - * possibly add it to the list of adapted arguments - */ - protected def addArg(arg: TypedArg, formal: Type): Unit - - /** Is this an argument of the form `expr: _*` or a RepeatedParamType - * derived from such an argument? - */ - protected def isVarArg(arg: Arg): Boolean - - /** If constructing trees, turn last `n` processed arguments into a - * `SeqLiteral` tree with element type `elemFormal`. - */ - protected def makeVarArg(n: Int, elemFormal: Type): Unit - - /** If all `args` have primitive numeric types, make sure it's the same one */ - protected def harmonizeArgs(args: List[TypedArg]): List[TypedArg] - - /** Signal failure with given message at position of given argument */ - protected def fail(msg: => Message, arg: Arg): Unit - - /** Signal failure with given message at position of the application itself */ - protected def fail(msg: => Message): Unit - - protected def appPos: Position - - /** The current function part, which might be affected by lifting. - */ - protected def normalizedFun: Tree - - /** If constructing trees, pull out all parts of the function - * which are not idempotent into separate prefix definitions - */ - protected def liftFun(): Unit = () - - /** A flag signalling that the typechecking the application was so far successful */ - private[this] var _ok = true - - def ok = _ok - def ok_=(x: Boolean) = { - assert(x || ctx.reporter.errorsReported || !ctx.typerState.isCommittable) // !!! DEBUG - _ok = x - } - - /** The function's type after widening and instantiating polytypes - * with polyparams in constraint set - */ - val methType = funType.widen match { - case funType: MethodType => funType - case funType: PolyType => constrained(funType).resultType - case tp => tp //was: funType - } - - /** The arguments re-ordered so that each named argument matches the - * same-named formal parameter. - */ - lazy val orderedArgs = - if (hasNamedArg(args)) - reorder(args.asInstanceOf[List[untpd.Tree]]).asInstanceOf[List[Arg]] - else - args - - protected def init() = methType match { - case methType: MethodType => - // apply the result type constraint, unless method type is dependent - if (!methType.isDependent) { - val savedConstraint = ctx.typerState.constraint - if (!constrainResult(methType.resultType, resultType)) - if (ctx.typerState.isCommittable) - // defer the problem until after the application; - // it might be healed by an implicit conversion - assert(ctx.typerState.constraint eq savedConstraint) - else - fail(err.typeMismatchMsg(methType.resultType, resultType)) - } - // match all arguments with corresponding formal parameters - matchArgs(orderedArgs, methType.paramTypes, 0) - case _ => - if (methType.isError) ok = false - else fail(s"$methString does not take parameters") - } - - /** The application was successful */ - def success = ok - - protected def methodType = methType.asInstanceOf[MethodType] - private def methString: String = i"${methRef.symbol}: ${methType.show}" - - /** Re-order arguments to correctly align named arguments */ - def reorder[T >: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = { - - /** @param pnames The list of parameter names that are missing arguments - * @param args The list of arguments that are not yet passed, or that are waiting to be dropped - * @param nameToArg A map from as yet unseen names to named arguments - * @param toDrop A set of names that have already be passed as named arguments - * - * For a well-typed application we have the invariants - * - * 1. `(args diff toDrop)` can be reordered to match `pnames` - * 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args` - */ - def recur(pnames: List[Name], args: List[Trees.Tree[T]], - nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name]): List[Trees.Tree[T]] = pnames match { - case pname :: pnames1 if nameToArg contains pname => - // there is a named argument for this parameter; pick it - nameToArg(pname) :: recur(pnames1, args, nameToArg - pname, toDrop + pname) - case _ => - def pnamesRest = if (pnames.isEmpty) pnames else pnames.tail - args match { - case (arg @ NamedArg(aname, _)) :: args1 => - if (toDrop contains aname) // argument is already passed - recur(pnames, args1, nameToArg, toDrop - aname) - else if ((nameToArg contains aname) && pnames.nonEmpty) // argument is missing, pass an empty tree - genericEmptyTree :: recur(pnames.tail, args, nameToArg, toDrop) - else { // name not (or no longer) available for named arg - def msg = - if (methodType.paramNames contains aname) - s"parameter $aname of $methString is already instantiated" - else - s"$methString does not have a parameter $aname" - fail(msg, arg.asInstanceOf[Arg]) - arg :: recur(pnamesRest, args1, nameToArg, toDrop) - } - case arg :: args1 => - arg :: recur(pnamesRest, args1, nameToArg, toDrop) // unnamed argument; pick it - case Nil => // no more args, continue to pick up any preceding named args - if (pnames.isEmpty) Nil - else recur(pnamesRest, args, nameToArg, toDrop) - } - } - val nameAssocs = for (arg @ NamedArg(name, _) <- args) yield (name, arg) - recur(methodType.paramNames, args, nameAssocs.toMap, Set()) - } - - /** Splice new method reference into existing application */ - def spliceMeth(meth: Tree, app: Tree): Tree = app match { - case Apply(fn, args) => Apply(spliceMeth(meth, fn), args) - case TypeApply(fn, targs) => TypeApply(spliceMeth(meth, fn), targs) - case _ => meth - } - - /** Find reference to default parameter getter for parameter #n in current - * parameter list, or NoType if none was found - */ - def findDefaultGetter(n: Int)(implicit ctx: Context): Tree = { - val meth = methRef.symbol.asTerm - val receiver: Tree = methPart(normalizedFun) match { - case Select(receiver, _) => receiver - case mr => mr.tpe.normalizedPrefix match { - case mr: TermRef => ref(mr) - case mr => - if (this.isInstanceOf[TestApplication[_]]) - // In this case it is safe to skolemize now; we will produce a stable prefix for the actual call. - ref(mr.narrow) - else - EmptyTree - } - } - val getterPrefix = - if ((meth is Synthetic) && meth.name == nme.apply) nme.CONSTRUCTOR else meth.name - def getterName = getterPrefix.defaultGetterName(n) - if (!meth.hasDefaultParams) - EmptyTree - else if (receiver.isEmpty) { - def findGetter(cx: Context): Tree = { - if (cx eq NoContext) EmptyTree - else if (cx.scope != cx.outer.scope && - cx.denotNamed(meth.name).hasAltWith(_.symbol == meth)) { - val denot = cx.denotNamed(getterName) - assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)") - ref(TermRef(cx.owner.thisType, getterName, denot)) - } else findGetter(cx.outer) - } - findGetter(ctx) - } - else { - def selectGetter(qual: Tree): Tree = { - val getterDenot = qual.tpe.member(getterName) - if (getterDenot.exists) qual.select(TermRef(qual.tpe, getterName, getterDenot)) - else EmptyTree - } - if (!meth.isClassConstructor) - selectGetter(receiver) - else { - // default getters for class constructors are found in the companion object - val cls = meth.owner - val companion = cls.companionModule - receiver.tpe.baseTypeRef(cls) match { - case tp: TypeRef if companion.isTerm => - selectGetter(ref(TermRef(tp.prefix, companion.asTerm))) - case _ => - EmptyTree - } - } - } - } - - /** Match re-ordered arguments against formal parameters - * @param n The position of the first parameter in formals in `methType`. - */ - def matchArgs(args: List[Arg], formals: List[Type], n: Int): Unit = { - if (success) formals match { - case formal :: formals1 => - - def addTyped(arg: Arg, formal: Type) = - addArg(typedArg(arg, formal), formal) - - def missingArg(n: Int): Unit = { - val pname = methodType.paramNames(n) - fail( - if (pname contains '$') s"not enough arguments for $methString" - else s"missing argument for parameter $pname of $methString") - } - - def tryDefault(n: Int, args1: List[Arg]): Unit = { - liftFun() - val getter = findDefaultGetter(n + numArgs(normalizedFun)) - if (getter.isEmpty) missingArg(n) - else { - addTyped(treeToArg(spliceMeth(getter withPos appPos, normalizedFun)), formal) - matchArgs(args1, formals1, n + 1) - } - } - - if (formal.isRepeatedParam) - args match { - case arg :: Nil if isVarArg(arg) => - addTyped(arg, formal) - case _ => - val elemFormal = formal.widenExpr.argTypesLo.head - val origConstraint = ctx.typerState.constraint - var typedArgs = args.map(typedArg(_, elemFormal)) - val harmonizedArgs = harmonizeArgs(typedArgs) - if (harmonizedArgs ne typedArgs) { - ctx.typerState.constraint = origConstraint - typedArgs = harmonizedArgs - } - typedArgs.foreach(addArg(_, elemFormal)) - makeVarArg(args.length, elemFormal) - } - else args match { - case EmptyTree :: args1 => - tryDefault(n, args1) - case arg :: args1 => - addTyped(arg, formal) - matchArgs(args1, formals1, n + 1) - case nil => - tryDefault(n, args) - } - - case nil => - args match { - case arg :: args1 => fail(s"too many arguments for $methString", arg) - case nil => - } - } - } - } - - /** Subclass of Application for the cases where we are interested only - * in a "can/cannot apply" answer, without needing to construct trees or - * issue error messages. - */ - abstract class TestApplication[Arg](methRef: TermRef, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) - extends Application[Arg](methRef, funType, args, resultType) { - type TypedArg = Arg - type Result = Unit - - /** The type of the given argument */ - protected def argType(arg: Arg, formal: Type): Type - - def typedArg(arg: Arg, formal: Type): Arg = arg - def addArg(arg: TypedArg, formal: Type) = - ok = ok & isCompatible(argType(arg, formal), formal) - def makeVarArg(n: Int, elemFormal: Type) = {} - def fail(msg: => Message, arg: Arg) = - ok = false - def fail(msg: => Message) = - ok = false - def appPos = NoPosition - lazy val normalizedFun = ref(methRef) - init() - } - - /** Subclass of Application for applicability tests with type arguments and value - * argument trees. - */ - class ApplicableToTrees(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) - extends TestApplication(methRef, methRef.widen.appliedTo(targs), args, resultType) { - def argType(arg: Tree, formal: Type): Type = normalize(arg.tpe, formal) - def treeToArg(arg: Tree): Tree = arg - def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg) - def harmonizeArgs(args: List[Tree]) = harmonize(args) - } - - /** Subclass of Application for applicability tests with type arguments and value - * argument trees. - */ - class ApplicableToTreesDirectly(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends ApplicableToTrees(methRef, targs, args, resultType)(ctx) { - override def addArg(arg: TypedArg, formal: Type) = - ok = ok & (argType(arg, formal) <:< formal) - } - - /** Subclass of Application for applicability tests with value argument types. */ - class ApplicableToTypes(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context) - extends TestApplication(methRef, methRef, args, resultType) { - def argType(arg: Type, formal: Type): Type = arg - def treeToArg(arg: Tree): Type = arg.tpe - def isVarArg(arg: Type): Boolean = arg.isRepeatedParam - def harmonizeArgs(args: List[Type]) = harmonizeTypes(args) - } - - /** Subclass of Application for type checking an Apply node, where - * types of arguments are either known or unknown. - */ - abstract class TypedApply[T >: Untyped]( - app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Trees.Tree[T]], resultType: Type)(implicit ctx: Context) - extends Application(methRef, fun.tpe, args, resultType) { - type TypedArg = Tree - def isVarArg(arg: Trees.Tree[T]): Boolean = untpd.isWildcardStarArg(arg) - private var typedArgBuf = new mutable.ListBuffer[Tree] - private var liftedDefs: mutable.ListBuffer[Tree] = null - private var myNormalizedFun: Tree = fun - init() - - def addArg(arg: Tree, formal: Type): Unit = - typedArgBuf += adaptInterpolated(arg, formal.widenExpr, EmptyTree) - - def makeVarArg(n: Int, elemFormal: Type): Unit = { - val args = typedArgBuf.takeRight(n).toList - typedArgBuf.trimEnd(n) - val elemtpt = TypeTree(elemFormal) - val seqLit = - if (methodType.isJava) JavaSeqLiteral(args, elemtpt) - else SeqLiteral(args, elemtpt) - typedArgBuf += seqToRepeated(seqLit) - } - - def harmonizeArgs(args: List[TypedArg]) = harmonize(args) - - override def appPos = app.pos - - def fail(msg: => Message, arg: Trees.Tree[T]) = { - ctx.error(msg, arg.pos) - ok = false - } - - def fail(msg: => Message) = { - ctx.error(msg, app.pos) - ok = false - } - - def normalizedFun = myNormalizedFun - - override def liftFun(): Unit = - if (liftedDefs == null) { - liftedDefs = new mutable.ListBuffer[Tree] - myNormalizedFun = liftApp(liftedDefs, myNormalizedFun) - } - - /** The index of the first difference between lists of trees `xs` and `ys`, - * where `EmptyTree`s in the second list are skipped. - * -1 if there are no differences. - */ - private def firstDiff[T <: Trees.Tree[_]](xs: List[T], ys: List[T], n: Int = 0): Int = xs match { - case x :: xs1 => - ys match { - case EmptyTree :: ys1 => firstDiff(xs1, ys1, n) - case y :: ys1 => if (x ne y) n else firstDiff(xs1, ys1, n + 1) - case nil => n - } - case nil => - ys match { - case EmptyTree :: ys1 => firstDiff(xs, ys1, n) - case y :: ys1 => n - case nil => -1 - } - } - private def sameSeq[T <: Trees.Tree[_]](xs: List[T], ys: List[T]): Boolean = firstDiff(xs, ys) < 0 - - val result = { - var typedArgs = typedArgBuf.toList - def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later - val app1 = - if (!success) app0.withType(ErrorType) - else { - if (!sameSeq(args, orderedArgs)) { - // need to lift arguments to maintain evaluation order in the - // presence of argument reorderings. - liftFun() - val eqSuffixLength = firstDiff(app.args.reverse, orderedArgs.reverse) - val (liftable, rest) = typedArgs splitAt (typedArgs.length - eqSuffixLength) - typedArgs = liftArgs(liftedDefs, methType, liftable) ++ rest - } - if (sameSeq(typedArgs, args)) // trick to cut down on tree copying - typedArgs = args.asInstanceOf[List[Tree]] - assignType(app0, normalizedFun, typedArgs) - } - wrapDefs(liftedDefs, app1) - } - } - - /** Subclass of Application for type checking an Apply node with untyped arguments. */ - class ApplyToUntyped(app: untpd.Apply, fun: Tree, methRef: TermRef, proto: FunProto, resultType: Type)(implicit ctx: Context) - extends TypedApply(app, fun, methRef, proto.args, resultType) { - def typedArg(arg: untpd.Tree, formal: Type): TypedArg = proto.typedArg(arg, formal.widenExpr) - def treeToArg(arg: Tree): untpd.Tree = untpd.TypedSplice(arg) - } - - /** Subclass of Application for type checking an Apply node with typed arguments. */ - class ApplyToTyped(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context) - extends TypedApply[Type](app, fun, methRef, args, resultType) { - // Dotty deviation: Dotc infers Untyped for the supercall. This seems to be according to the rules - // (of both Scala and Dotty). Untyped is legal, and a subtype of Typed, whereas TypeApply - // is invariant in the type parameter, so the minimal type should be inferred. But then typedArg does - // not match the abstract method in Application and an abstract class error results. - def typedArg(arg: tpd.Tree, formal: Type): TypedArg = arg - def treeToArg(arg: Tree): Tree = arg - } - - /** If `app` is a `this(...)` constructor call, the this-call argument context, - * otherwise the current context. - */ - def argCtx(app: untpd.Tree)(implicit ctx: Context): Context = - if (untpd.isSelfConstrCall(app)) ctx.thisCallArgContext else ctx - - def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { - - def realApply(implicit ctx: Context): Tree = track("realApply") { - val originalProto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree)) - val fun1 = typedExpr(tree.fun, originalProto) - - // Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as - // a side effect in adapt. If it was, we assume the tupled proto-type in the rest of the application, - // until, possibly, we have to fall back to insert an implicit on the qualifier. - // This crucially relies on he fact that `proto` is used only in a single call of `adapt`, - // otherwise we would get possible cross-talk between different `adapt` calls using the same - // prototype. A cleaner alternative would be to return a modified prototype from `adapt` together with - // a modified tree but this would be more convoluted and less efficient. - val proto = if (originalProto.isTupled) originalProto.tupled else originalProto - - // If some of the application's arguments are function literals without explicitly declared - // parameter types, relate the normalized result type of the application with the - // expected type through `constrainResult`. This can add more constraints which - // help sharpen the inferred parameter types for the argument function literal(s). - // This tweak is needed to make i1378 compile. - if (tree.args.exists(untpd.isFunctionWithUnknownParamType(_))) - if (!constrainResult(fun1.tpe.widen, proto.derivedFunProto(resultType = pt))) - typr.println(i"result failure for $tree with type ${fun1.tpe.widen}, expected = $pt") - - /** Type application where arguments come from prototype, and no implicits are inserted */ - def simpleApply(fun1: Tree, proto: FunProto)(implicit ctx: Context): Tree = - methPart(fun1).tpe match { - case funRef: TermRef => - val app = - if (proto.allArgTypesAreCurrent()) - new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) - else - new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree)) - convertNewGenericArray(ConstFold(app.result)) - case _ => - handleUnexpectedFunType(tree, fun1) - } - - /** Try same application with an implicit inserted around the qualifier of the function - * part. Return an optional value to indicate success. - */ - def tryWithImplicitOnQualifier(fun1: Tree, proto: FunProto)(implicit ctx: Context): Option[Tree] = - tryInsertImplicitOnQualifier(fun1, proto) flatMap { fun2 => - tryEither { - implicit ctx => Some(simpleApply(fun2, proto)): Option[Tree] - } { - (_, _) => None - } - } - - fun1.tpe match { - case ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(ErrorType) - case TryDynamicCallType => typedDynamicApply(tree, pt) - case _ => - tryEither { - implicit ctx => simpleApply(fun1, proto) - } { - (failedVal, failedState) => - def fail = { failedState.commit(); failedVal } - // Try once with original prototype and once (if different) with tupled one. - // The reason we need to try both is that the decision whether to use tupled - // or not was already taken but might have to be revised when an implicit - // is inserted on the qualifier. - tryWithImplicitOnQualifier(fun1, originalProto).getOrElse( - if (proto eq originalProto) fail - else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail)) - } - } - } - - /** Convert expression like - * - * e += (args) - * - * where the lifted-for-assignment version of e is { val xs = es; e' } to - * - * { val xs = es; e' = e' + args } - */ - def typedOpAssign: Tree = track("typedOpAssign") { - val Apply(Select(lhs, name), rhss) = tree - val lhs1 = typedExpr(lhs) - val liftedDefs = new mutable.ListBuffer[Tree] - val lhs2 = untpd.TypedSplice(liftAssigned(liftedDefs, lhs1)) - val assign = untpd.Assign(lhs2, untpd.Apply(untpd.Select(lhs2, name.init), rhss)) - wrapDefs(liftedDefs, typed(assign)) - } - - if (untpd.isOpAssign(tree)) - tryEither { - implicit ctx => realApply - } { (failedVal, failedState) => - tryEither { - implicit ctx => typedOpAssign - } { (_, _) => - failedState.commit() - failedVal - } - } - else { - val app = realApply - app match { - case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType => - val op = fn.symbol - if (op == defn.Any_== || op == defn.Any_!=) - checkCanEqual(left.tpe.widen, right.tpe.widen, app.pos) - case _ => - } - app - } - } - - /** Overridden in ReTyper to handle primitive operations that can be generated after erasure */ - protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = - throw new Error(i"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}") - - def typedNamedArgs(args: List[untpd.Tree])(implicit ctx: Context) = - for (arg @ NamedArg(id, argtpt) <- args) yield { - val argtpt1 = typedType(argtpt) - cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe) - } - - def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = track("typedTypeApply") { - val isNamed = hasNamedArg(tree.args) - val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) - val typedFn = typedExpr(tree.fun, PolyProto(typedArgs.tpes, pt)) - typedFn.tpe.widen match { - case pt: PolyType => - if (typedArgs.length <= pt.paramBounds.length && !isNamed) - if (typedFn.symbol == defn.Predef_classOf && typedArgs.nonEmpty) { - val arg = typedArgs.head - checkClassType(arg.tpe, arg.pos, traitReq = false, stablePrefixReq = false) - } - case _ => - } - def tryDynamicTypeApply(): Tree = typedFn match { - case typedFn: Select if !pt.isInstanceOf[FunProto] => typedDynamicSelect(typedFn, typedArgs, pt) - case _ => tree.withType(TryDynamicCallType) - } - if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply() - else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs) - } - - /** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray. - * It is performed during typer as creation of generic arrays needs a classTag. - * we rely on implicit search to find one. - */ - def convertNewGenericArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { - case Apply(TypeApply(tycon, targs@(targ :: Nil)), args) if tycon.symbol == defn.ArrayConstructor => - fullyDefinedType(tree.tpe, "array", tree.pos) - - def newGenericArrayCall = - ref(defn.DottyArraysModule) - .select(defn.newGenericArrayMethod).withPos(tree.pos) - .appliedToTypeTrees(targs).appliedToArgs(args) - - if (TypeErasure.isUnboundedGeneric(targ.tpe)) - newGenericArrayCall - else tree - case _ => - tree - } - - def typedUnApply(tree: untpd.Apply, selType: Type)(implicit ctx: Context): Tree = track("typedUnApply") { - val Apply(qual, args) = tree - - def notAnExtractor(tree: Tree) = - errorTree(tree, s"${qual.show} cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method") - - /** If this is a term ref tree, try to typecheck with its type name. - * If this refers to a type alias, follow the alias, and if - * one finds a class, reference the class companion module. - */ - def followTypeAlias(tree: untpd.Tree): untpd.Tree = { - tree match { - case tree: untpd.RefTree => - val ttree = typedType(untpd.rename(tree, tree.name.toTypeName)) - ttree.tpe match { - case alias: TypeRef if alias.info.isAlias => - companionRef(alias) match { - case companion: TermRef => return untpd.ref(companion) withPos tree.pos - case _ => - } - case _ => - } - case _ => - } - untpd.EmptyTree - } - - /** A typed qual.unapply or qual.unapplySeq tree, if this typechecks. - * Otherwise fallBack with (maltyped) qual.unapply as argument - * Note: requires special handling for overloaded occurrences of - * unapply or unapplySeq. We first try to find a non-overloaded - * method which matches any type. If that fails, we try to find an - * overloaded variant which matches one of the argument types. - * In fact, overloaded unapply's are problematic because a non- - * overloaded unapply does *not* need to be applicable to its argument - * whereas overloaded variants need to have a conforming variant. - */ - def trySelectUnapply(qual: untpd.Tree)(fallBack: Tree => Tree): Tree = { - val genericProto = new UnapplyFunProto(WildcardType, this) - def specificProto = new UnapplyFunProto(selType, this) - // try first for non-overloaded, then for overloaded ocurrences - def tryWithName(name: TermName)(fallBack: Tree => Tree)(implicit ctx: Context): Tree = - tryEither { - implicit ctx => typedExpr(untpd.Select(qual, name), specificProto) - } { - (sel, _) => - tryEither { - implicit ctx => typedExpr(untpd.Select(qual, name), genericProto) - } { - (_, _) => fallBack(sel) - } - } - // try first for unapply, then for unapplySeq - tryWithName(nme.unapply) { - sel => tryWithName(nme.unapplySeq)(_ => fallBack(sel)) // for backwards compatibility; will be dropped - } - } - - /** Produce a typed qual.unapply or qual.unapplySeq tree, or - * else if this fails follow a type alias and try again. - */ - val unapplyFn = trySelectUnapply(qual) { sel => - val qual1 = followTypeAlias(qual) - if (qual1.isEmpty) notAnExtractor(sel) - else trySelectUnapply(qual1)(_ => notAnExtractor(sel)) - } - - def fromScala2x = unapplyFn.symbol.exists && (unapplyFn.symbol.owner is Scala2x) - - /** Is `subtp` a subtype of `tp` or of some generalization of `tp`? - * The generalizations of a type T are the smallest set G such that - * - * - T is in G - * - If a typeref R in G represents a class or trait, R's superclass is in G. - * - If a type proxy P is not a reference to a class, P's supertype is in G - */ - def isSubTypeOfParent(subtp: Type, tp: Type)(implicit ctx: Context): Boolean = - if (subtp <:< tp) true - else tp match { - case tp: TypeRef if tp.symbol.isClass => isSubTypeOfParent(subtp, tp.firstParent) - case tp: TypeProxy => isSubTypeOfParent(subtp, tp.superType) - case _ => false - } - - unapplyFn.tpe.widen match { - case mt: MethodType if mt.paramTypes.length == 1 => - val unapplyArgType = mt.paramTypes.head - unapp.println(i"unapp arg tpe = $unapplyArgType, pt = $selType") - val ownType = - if (selType <:< unapplyArgType) { - unapp.println(i"case 1 $unapplyArgType ${ctx.typerState.constraint}") - selType - } else if (isSubTypeOfParent(unapplyArgType, selType)(ctx.addMode(Mode.GADTflexible))) { - maximizeType(unapplyArgType) match { - case Some(tvar) => - def msg = - ex"""There is no best instantiation of pattern type $unapplyArgType - |that makes it a subtype of selector type $selType. - |Non-variant type variable ${tvar.origin} cannot be uniquely instantiated.""" - if (fromScala2x) { - // We can't issue an error here, because in Scala 2, ::[B] is invariant - // whereas List[+T] is covariant. According to the strict rule, a pattern - // match of a List[C] against a case x :: xs is illegal, because - // B cannot be uniquely instantiated. Of course :: should have been - // covariant in the first place, but in the Scala libraries it isn't. - // So for now we allow these kinds of patterns, even though they - // can open unsoundness holes. See SI-7952 for an example of the hole this opens. - if (ctx.settings.verbose.value) ctx.warning(msg, tree.pos) - } else { - unapp.println(s" ${unapplyFn.symbol.owner} ${unapplyFn.symbol.owner is Scala2x}") - ctx.strictWarning(msg, tree.pos) - } - case _ => - } - unapp.println(i"case 2 $unapplyArgType ${ctx.typerState.constraint}") - unapplyArgType - } else { - unapp.println("Neither sub nor super") - unapp.println(TypeComparer.explained(implicit ctx => unapplyArgType <:< selType)) - errorType( - ex"Pattern type $unapplyArgType is neither a subtype nor a supertype of selector type $selType", - tree.pos) - } - - val dummyArg = dummyTreeOfType(ownType) - val unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil))) - val unapplyImplicits = unapplyApp match { - case Apply(Apply(unapply, `dummyArg` :: Nil), args2) => assert(args2.nonEmpty); args2 - case Apply(unapply, `dummyArg` :: Nil) => Nil - } - - var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.pos) - for (argType <- argTypes) assert(!argType.isInstanceOf[TypeBounds], unapplyApp.tpe.show) - val bunchedArgs = argTypes match { - case argType :: Nil => - if (argType.isRepeatedParam) untpd.SeqLiteral(args, untpd.TypeTree()) :: Nil - else if (args.lengthCompare(1) > 0 && ctx.canAutoTuple) untpd.Tuple(args) :: Nil - else args - case _ => args - } - if (argTypes.length != bunchedArgs.length) { - ctx.error(em"wrong number of argument patterns for $qual; expected: ($argTypes%, %)", tree.pos) - argTypes = argTypes.take(args.length) ++ - List.fill(argTypes.length - args.length)(WildcardType) - } - val unapplyPatterns = (bunchedArgs, argTypes).zipped map (typed(_, _)) - val result = assignType(cpy.UnApply(tree)(unapplyFn, unapplyImplicits, unapplyPatterns), ownType) - unapp.println(s"unapply patterns = $unapplyPatterns") - if ((ownType eq selType) || ownType.isError) result - else Typed(result, TypeTree(ownType)) - case tp => - val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn) - val typedArgsErr = args mapconserve (typed(_, defn.AnyType)) - cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType ErrorType - } - } - - /** A typed unapply hook, can be overridden by re any-typers between frontend - * and pattern matcher. - */ - def typedUnApply(tree: untpd.UnApply, selType: Type)(implicit ctx: Context): UnApply = - throw new UnsupportedOperationException("cannot type check an UnApply node") - - /** Is given method reference applicable to type arguments `targs` and argument trees `args`? - * @param resultType The expected result type of the application - */ - def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = { - val nestedContext = ctx.fresh.setExploreTyperState - new ApplicableToTrees(methRef, targs, args, resultType)(nestedContext).success - } - - /** Is given method reference applicable to type arguments `targs` and argument trees `args` without inferring views? - * @param resultType The expected result type of the application - */ - def isDirectlyApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = { - val nestedContext = ctx.fresh.setExploreTyperState - new ApplicableToTreesDirectly(methRef, targs, args, resultType)(nestedContext).success - } - - /** Is given method reference applicable to argument types `args`? - * @param resultType The expected result type of the application - */ - def isApplicable(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean = { - val nestedContext = ctx.fresh.setExploreTyperState - new ApplicableToTypes(methRef, args, resultType)(nestedContext).success - } - - /** Is given type applicable to type arguments `targs` and argument trees `args`, - * possibly after inserting an `apply`? - * @param resultType The expected result type of the application - */ - def isApplicable(tp: Type, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = - onMethod(tp, isApplicable(_, targs, args, resultType)) - - /** Is given type applicable to argument types `args`, possibly after inserting an `apply`? - * @param resultType The expected result type of the application - */ - def isApplicable(tp: Type, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean = - onMethod(tp, isApplicable(_, args, resultType)) - - private def onMethod(tp: Type, p: TermRef => Boolean)(implicit ctx: Context): Boolean = tp match { - case methRef: TermRef if methRef.widenSingleton.isInstanceOf[MethodicType] => - p(methRef) - case mt: MethodicType => - p(mt.narrow) - case _ => - tp.member(nme.apply).hasAltWith(d => p(TermRef(tp, nme.apply, d))) - } - - /** In a set of overloaded applicable alternatives, is `alt1` at least as good as - * `alt2`? `alt1` and `alt2` are non-overloaded references. - */ - def isAsGood(alt1: TermRef, alt2: TermRef)(implicit ctx: Context): Boolean = track("isAsGood") { ctx.traceIndented(i"isAsGood($alt1, $alt2)", overload) { - - assert(alt1 ne alt2) - - /** Is class or module class `sym1` derived from class or module class `sym2`? - * Module classes also inherit the relationship from their companions. - */ - def isDerived(sym1: Symbol, sym2: Symbol): Boolean = - if (sym1 isSubClass sym2) true - else if (sym2 is Module) isDerived(sym1, sym2.companionClass) - else (sym1 is Module) && isDerived(sym1.companionClass, sym2) - - /** Is alternative `alt1` with type `tp1` as specific as alternative - * `alt2` with type `tp2` ? - * - * 1. A method `alt1` of type (p1: T1, ..., pn: Tn)U is as specific as `alt2` - * if `alt2` is applicable to arguments (p1, ..., pn) of types T1,...,Tn - * or if `alt1` is nullary. - * 2. A polymorphic member of type [a1 >: L1 <: U1, ..., an >: Ln <: Un]T is as - * specific as `alt2` of type `tp2` if T is as specific as `tp2` under the - * assumption that for i = 1,...,n each ai is an abstract type name bounded - * from below by Li and from above by Ui. - * 3. A member of any other type `tp1` is: - * a. always as specific as a method or a polymorphic method. - * b. as specific as a member of any other type `tp2` if `tp1` is compatible - * with `tp2`. - */ - def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = ctx.traceIndented(i"isAsSpecific $tp1 $tp2", overload) { tp1 match { - case tp1: MethodType => // (1) - def repeatedToSingle(tp: Type): Type = tp match { - case tp @ ExprType(tp1) => tp.derivedExprType(repeatedToSingle(tp1)) - case _ => if (tp.isRepeatedParam) tp.argTypesHi.head else tp - } - val formals1 = - if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramTypes map repeatedToSingle - else tp1.paramTypes - isApplicable(alt2, formals1, WildcardType) || - tp1.paramTypes.isEmpty && tp2.isInstanceOf[MethodOrPoly] - case tp1: PolyType => // (2) - val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds) - isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2) - case _ => // (3) - tp2 match { - case tp2: MethodType => true // (3a) - case tp2: PolyType if tp2.isPolymorphicMethodType => true // (3a) - case tp2: PolyType => // (3b) - val nestedCtx = ctx.fresh.setExploreTyperState - - { - implicit val ctx: Context = nestedCtx - isAsSpecificValueType(tp1, constrained(tp2).resultType) - } - case _ => // (3b) - isAsSpecificValueType(tp1, tp2) - } - }} - - /** Test whether value type `tp1` is as specific as value type `tp2`. - * Let's abbreviate this to `tp1 <:s tp2`. - * Previously, `<:s` was the same as `<:`. This behavior is still - * available under mode `Mode.OldOverloadingResolution`. The new behavior - * is different, however. Here, `T <:s U` iff - * - * flip(T) <: flip(U) - * - * where `flip` changes top-level contravariant type aliases to covariant ones. - * Intuitively `<:s` means subtyping `<:`, except that all top-level arguments - * to contravariant parameters are compared as if they were covariant. E.g. given class - * - * class Cmp[-X] - * - * `Cmp[T] <:s Cmp[U]` if `T <: U`. On the other hand, nested occurrences - * of parameters are not affected. - * So `T <: U` would imply `List[Cmp[U]] <:s List[Cmp[T]]`, as usual. - * - * This relation might seem strange, but it models closely what happens for methods. - * Indeed, if we integrate the existing rules for methods into `<:s` we have now that - * - * (T)R <:s (U)R - * - * iff - * - * T => R <:s U => R - */ - def isAsSpecificValueType(tp1: Type, tp2: Type)(implicit ctx: Context) = - if (ctx.mode.is(Mode.OldOverloadingResolution)) - isCompatible(tp1, tp2) - else { - val flip = new TypeMap { - def apply(t: Type) = t match { - case t: TypeAlias if variance > 0 && t.variance < 0 => t.derivedTypeAlias(t.alias, 1) - case t: TypeBounds => t - case _ => mapOver(t) - } - } - isCompatible(flip(tp1), flip(tp2)) - } - - /** Drop any implicit parameter section */ - def stripImplicit(tp: Type): Type = tp match { - case mt: ImplicitMethodType if !mt.isDependent => - mt.resultType - // todo: make sure implicit method types are not dependent? - // but check test case in /tests/pos/depmet_implicit_chaining_zw.scala - case pt: PolyType => - pt.derivedPolyType(pt.paramNames, pt.paramBounds, stripImplicit(pt.resultType)) - case _ => - tp - } - - val owner1 = if (alt1.symbol.exists) alt1.symbol.owner else NoSymbol - val owner2 = if (alt2.symbol.exists) alt2.symbol.owner else NoSymbol - val tp1 = stripImplicit(alt1.widen) - val tp2 = stripImplicit(alt2.widen) - - def winsOwner1 = isDerived(owner1, owner2) - def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2) - def winsOwner2 = isDerived(owner2, owner1) - def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1) - - overload.println(i"isAsGood($alt1, $alt2)? $tp1 $tp2 $winsOwner1 $winsType1 $winsOwner2 $winsType2") - - // Assume the following probabilities: - // - // P(winsOwnerX) = 2/3 - // P(winsTypeX) = 1/3 - // - // Then the call probabilities of the 4 basic operations are as follows: - // - // winsOwner1: 1/1 - // winsOwner2: 1/1 - // winsType1 : 7/9 - // winsType2 : 4/9 - - if (winsOwner1) /* 6/9 */ !winsOwner2 || /* 4/9 */ winsType1 || /* 8/27 */ !winsType2 - else if (winsOwner2) /* 2/9 */ winsType1 && /* 2/27 */ !winsType2 - else /* 1/9 */ winsType1 || /* 2/27 */ !winsType2 - }} - - def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") { - alts match { - case Nil => alts - case _ :: Nil => alts - case alt :: alts1 => - def winner(bestSoFar: TermRef, alts: List[TermRef]): TermRef = alts match { - case alt :: alts1 => - winner(if (isAsGood(alt, bestSoFar)) alt else bestSoFar, alts1) - case nil => - bestSoFar - } - val best = winner(alt, alts1) - def asGood(alts: List[TermRef]): List[TermRef] = alts match { - case alt :: alts1 => - if ((alt eq best) || !isAsGood(alt, best)) asGood(alts1) - else alt :: asGood(alts1) - case nil => - Nil - } - best :: asGood(alts) - } - } - - /** Resolve overloaded alternative `alts`, given expected type `pt` and - * possibly also type argument `targs` that need to be applied to each alternative - * to form the method type. - * todo: use techniques like for implicits to pick candidates quickly? - */ - def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { - - /** Is `alt` a method or polytype whose result type after the first value parameter - * section conforms to the expected type `resultType`? If `resultType` - * is a `IgnoredProto`, pick the underlying type instead. - */ - def resultConforms(alt: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match { - case IgnoredProto(ignored) => resultConforms(alt, ignored) - case _: ValueType => - alt.widen match { - case tp: PolyType => resultConforms(constrained(tp).resultType, resultType) - case tp: MethodType => constrainResult(tp.resultType, resultType) - case _ => true - } - case _ => true - } - - /** If the `chosen` alternative has a result type incompatible with the expected result - * type `pt`, run overloading resolution again on all alternatives that do match `pt`. - * If the latter succeeds with a single alternative, return it, otherwise - * fallback to `chosen`. - * - * Note this order of events is done for speed. One might be tempted to - * preselect alternatives by result type. But is slower, because it discriminates - * less. The idea is when searching for a best solution, as is the case in overloading - * resolution, we should first try criteria which are cheap and which have a high - * probability of pruning the search. result type comparisons are neither cheap nor - * do they prune much, on average. - */ - def adaptByResult(chosen: TermRef) = { - def nestedCtx = ctx.fresh.setExploreTyperState - pt match { - case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) => - alts.filter(alt => - (alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match { - case Nil => chosen - case alt2 :: Nil => alt2 - case alts2 => - resolveOverloaded(alts2, pt) match { - case alt2 :: Nil => alt2 - case _ => chosen - } - } - case _ => chosen - } - } - - var found = resolveOverloaded(alts, pt, Nil)(ctx.retractMode(Mode.ImplicitsEnabled)) - if (found.isEmpty && ctx.mode.is(Mode.ImplicitsEnabled)) - found = resolveOverloaded(alts, pt, Nil) - found match { - case alt :: Nil => adaptByResult(alt) :: Nil - case _ => found - } - } - - /** This private version of `resolveOverloaded` does the bulk of the work of - * overloading resolution, but does not do result adaptation. It might be - * called twice from the public `resolveOverloaded` method, once with - * implicits enabled, and once without. - */ - private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { - - def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty - - /** The shape of given tree as a type; cannot handle named arguments. */ - def typeShape(tree: untpd.Tree): Type = tree match { - case untpd.Function(args, body) => - defn.FunctionOf(args map Function.const(defn.AnyType), typeShape(body)) - case _ => - defn.NothingType - } - - /** The shape of given tree as a type; is more expensive than - * typeShape but can can handle named arguments. - */ - def treeShape(tree: untpd.Tree): Tree = tree match { - case NamedArg(name, arg) => - val argShape = treeShape(arg) - cpy.NamedArg(tree)(name, argShape).withType(argShape.tpe) - case _ => - dummyTreeOfType(typeShape(tree)) - } - - def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] = - alts filter (isApplicable(_, argTypes, resultType)) - - val candidates = pt match { - case pt @ FunProto(args, resultType, _) => - val numArgs = args.length - val normArgs = args.mapConserve { - case Block(Nil, expr) => expr - case x => x - } - - def sizeFits(alt: TermRef, tp: Type): Boolean = tp match { - case tp: PolyType => sizeFits(alt, tp.resultType) - case MethodType(_, ptypes) => - val numParams = ptypes.length - def isVarArgs = ptypes.nonEmpty && ptypes.last.isRepeatedParam - def hasDefault = alt.symbol.hasDefaultParams - if (numParams == numArgs) true - else if (numParams < numArgs) isVarArgs - else if (numParams > numArgs + 1) hasDefault - else isVarArgs || hasDefault - case _ => - numArgs == 0 - } - - def narrowBySize(alts: List[TermRef]): List[TermRef] = - alts filter (alt => sizeFits(alt, alt.widen)) - - def narrowByShapes(alts: List[TermRef]): List[TermRef] = { - if (normArgs exists (_.isInstanceOf[untpd.Function])) - if (hasNamedArg(args)) narrowByTrees(alts, args map treeShape, resultType) - else narrowByTypes(alts, normArgs map typeShape, resultType) - else - alts - } - - def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = { - val alts2 = alts.filter(alt => - isDirectlyApplicable(alt, targs, args, resultType) - ) - if (alts2.isEmpty && !ctx.isAfterTyper) - alts.filter(alt => - isApplicable(alt, targs, args, resultType) - ) - else - alts2 - } - - val alts1 = narrowBySize(alts) - //ctx.log(i"narrowed by size: ${alts1.map(_.symbol.showDcl)}%, %") - if (isDetermined(alts1)) alts1 - else { - val alts2 = narrowByShapes(alts1) - //ctx.log(i"narrowed by shape: ${alts1.map(_.symbol.showDcl)}%, %") - if (isDetermined(alts2)) alts2 - else { - pretypeArgs(alts2, pt) - narrowByTrees(alts2, pt.typedArgs, resultType) - } - } - - case pt @ PolyProto(targs1, pt1) => - assert(targs.isEmpty) - val alts1 = alts filter pt.isMatchedBy - resolveOverloaded(alts1, pt1, targs1) - - case defn.FunctionOf(args, resultType) => - narrowByTypes(alts, args, resultType) - - case pt => - alts filter (normalizedCompatible(_, pt)) - } - val found = narrowMostSpecific(candidates) - if (found.length <= 1) found - else { - val noDefaults = alts.filter(!_.symbol.hasDefaultParams) - if (noDefaults.length == 1) noDefaults // return unique alternative without default parameters if it exists - else { - val deepPt = pt.deepenProto - if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs) - else alts - } - } - } - - /** Try to typecheck any arguments in `pt` that are function values missing a - * parameter type. The expected type for these arguments is the lub of the - * corresponding formal parameter types of all alternatives. Type variables - * in formal parameter types are replaced by wildcards. The result of the - * typecheck is stored in `pt`, to be retrieved when its `typedArgs` are selected. - * The benefit of doing this is to allow idioms like this: - * - * def map(f: Char => Char): String = ??? - * def map[U](f: Char => U): Seq[U] = ??? - * map(x => x.toUpper) - * - * Without `pretypeArgs` we'd get a "missing parameter type" error for `x`. - * With `pretypeArgs`, we use the union of the two formal parameter types - * `Char => Char` and `Char => ?` as the expected type of the closure `x => x.toUpper`. - * That union is `Char => Char`, so we have an expected parameter type `Char` - * for `x`, and the code typechecks. - */ - private def pretypeArgs(alts: List[TermRef], pt: FunProto)(implicit ctx: Context): Unit = { - def recur(altFormals: List[List[Type]], args: List[untpd.Tree]): Unit = args match { - case arg :: args1 if !altFormals.exists(_.isEmpty) => - def isUnknownParamType(t: untpd.Tree) = t match { - case ValDef(_, tpt, _) => tpt.isEmpty - case _ => false - } - arg match { - case arg: untpd.Function if arg.args.exists(isUnknownParamType) => - def isUniform[T](xs: List[T])(p: (T, T) => Boolean) = xs.forall(p(_, xs.head)) - val formalsForArg: List[Type] = altFormals.map(_.head) - // For alternatives alt_1, ..., alt_n, test whether formal types for current argument are of the form - // (p_1_1, ..., p_m_1) => r_1 - // ... - // (p_1_n, ..., p_m_n) => r_n - val decomposedFormalsForArg: List[Option[(List[Type], Type)]] = - formalsForArg.map(defn.FunctionOf.unapply) - if (decomposedFormalsForArg.forall(_.isDefined)) { - val formalParamTypessForArg: List[List[Type]] = - decomposedFormalsForArg.map(_.get._1) - if (isUniform(formalParamTypessForArg)((x, y) => x.length == y.length)) { - val commonParamTypes = formalParamTypessForArg.transpose.map(ps => - // Given definitions above, for i = 1,...,m, - // ps(i) = List(p_i_1, ..., p_i_n) -- i.e. a column - // If all p_i_k's are the same, assume the type as formal parameter - // type of the i'th parameter of the closure. - if (isUniform(ps)(ctx.typeComparer.isSameTypeWhenFrozen(_, _))) ps.head - else WildcardType) - val commonFormal = defn.FunctionOf(commonParamTypes, WildcardType) - overload.println(i"pretype arg $arg with expected type $commonFormal") - pt.typedArg(arg, commonFormal) - } - } - case _ => - } - recur(altFormals.map(_.tail), args1) - case _ => - } - def paramTypes(alt: Type): List[Type] = alt match { - case mt: MethodType => mt.paramTypes - case mt: PolyType => paramTypes(mt.resultType) - case _ => Nil - } - recur(alts.map(alt => paramTypes(alt.widen)), pt.args) - } - - private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = { - def numericClasses(ts: List[T], acc: Set[Symbol]): Set[Symbol] = ts match { - case t :: ts1 => - val sym = tpe(t).widen.classSymbol - if (sym.isNumericValueClass) numericClasses(ts1, acc + sym) - else Set() - case Nil => - acc - } - val clss = numericClasses(ts, Set()) - if (clss.size > 1) { - val lub = defn.ScalaNumericValueTypeList.find(lubTpe => - clss.forall(cls => defn.isValueSubType(cls.typeRef, lubTpe))).get - ts.mapConserve(adapt(_, lub)) - } - else ts - } - - /** If `trees` all have numeric value types, and they do not have all the same type, - * pick a common numeric supertype and convert all trees to this type. - */ - def harmonize(trees: List[Tree])(implicit ctx: Context): List[Tree] = { - def adapt(tree: Tree, pt: Type): Tree = tree match { - case cdef: CaseDef => tpd.cpy.CaseDef(cdef)(body = adapt(cdef.body, pt)) - case _ => adaptInterpolated(tree, pt, tree) - } - if (ctx.isAfterTyper) trees else harmonizeWith(trees)(_.tpe, adapt) - } - - /** If all `types` are numeric value types, and they are not all the same type, - * pick a common numeric supertype and return it instead of every original type. - */ - def harmonizeTypes(tpes: List[Type])(implicit ctx: Context): List[Type] = - harmonizeWith(tpes)(identity, (tp, pt) => pt) -} - diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala deleted file mode 100644 index dbfc89f6c..000000000 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ /dev/null @@ -1,557 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast._ -import Contexts._ -import Types._ -import Flags._ -import Denotations._ -import Names._ -import StdNames._ -import NameOps._ -import Symbols._ -import Trees._ -import ProtoTypes._ -import Constants._ -import Scopes._ -import CheckRealizable._ -import ErrorReporting.errorTree -import annotation.unchecked -import util.Positions._ -import util.{Stats, SimpleMap} -import util.common._ -import transform.SymUtils._ -import Decorators._ -import Uniques._ -import ErrorReporting.{err, errorType} -import config.Printers.typr -import collection.mutable -import SymDenotations.NoCompleter - -object Checking { - import tpd._ - - /** A general checkBounds method that can be used for TypeApply nodes as - * well as for AppliedTypeTree nodes. Also checks that type arguments to - * *-type parameters are fully applied. - */ - def checkBounds(args: List[tpd.Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context): Unit = { - (args, boundss).zipped.foreach { (arg, bound) => - if (!bound.isHK && arg.tpe.isHK) - ctx.error(ex"missing type parameter(s) for $arg", arg.pos) - } - for ((arg, which, bound) <- ctx.boundsViolations(args, boundss, instantiate)) - ctx.error( - ex"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}", - arg.pos.focus) - } - - /** Check that type arguments `args` conform to corresponding bounds in `poly` - * Note: This does not check the bounds of AppliedTypeTrees. These - * are handled by method checkBounds in FirstTransform - */ - def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = - checkBounds(args, poly.paramBounds, _.substParams(poly, _)) - - /** Check applied type trees for well-formedness. This means - * - all arguments are within their corresponding bounds - * - if type is a higher-kinded application with wildcard arguments, - * check that it or one of its supertypes can be reduced to a normal application. - * Unreducible applications correspond to general existentials, and we - * cannot handle those. - */ - def checkAppliedType(tree: AppliedTypeTree)(implicit ctx: Context) = { - val AppliedTypeTree(tycon, args) = tree - // If `args` is a list of named arguments, return corresponding type parameters, - // otherwise return type parameters unchanged - val tparams = tycon.tpe.typeParams - def argNamed(tparam: TypeParamInfo) = args.find { - case NamedArg(name, _) => name == tparam.paramName - case _ => false - }.getOrElse(TypeTree(tparam.paramRef)) - val orderedArgs = if (hasNamedArg(args)) tparams.map(argNamed) else args - val bounds = tparams.map(_.paramBoundsAsSeenFrom(tycon.tpe)) - def instantiate(bound: Type, args: List[Type]) = - bound.LambdaAbstract(tparams).appliedTo(args) - checkBounds(orderedArgs, bounds, instantiate) - - def checkWildcardHKApply(tp: Type, pos: Position): Unit = tp match { - case tp @ HKApply(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) => - tycon match { - case tycon: PolyType => - ctx.errorOrMigrationWarning( - ex"unreducible application of higher-kinded type $tycon to wildcard arguments", - pos) - case _ => - checkWildcardHKApply(tp.superType, pos) - } - case _ => - } - def checkValidIfHKApply(implicit ctx: Context): Unit = - checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) - checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) - } - - /** Check that `tp` refers to a nonAbstract class - * and that the instance conforms to the self type of the created class. - */ - def checkInstantiable(tp: Type, pos: Position)(implicit ctx: Context): Unit = - tp.underlyingClassRef(refinementOK = false) match { - case tref: TypeRef => - val cls = tref.symbol - if (cls.is(AbstractOrTrait)) - ctx.error(em"$cls is abstract; cannot be instantiated", pos) - if (!cls.is(Module)) { - // Create a synthetic singleton type instance, and check whether - // it conforms to the self type of the class as seen from that instance. - val stp = SkolemType(tp) - val selfType = tref.givenSelfType.asSeenFrom(stp, cls) - if (selfType.exists && !(stp <:< selfType)) - ctx.error(ex"$tp does not conform to its self type $selfType; cannot be instantiated") - } - case _ => - } - - /** Check that type `tp` is realizable. */ - def checkRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = { - val rstatus = realizability(tp) - if (rstatus ne Realizable) { - def msg = em"$tp is not a legal path\n since it${rstatus.msg}" - if (ctx.scala2Mode) ctx.migrationWarning(msg, pos) else ctx.error(msg, pos) - } - } - - /** A type map which checks that the only cycles in a type are F-bounds - * and that protects all F-bounded references by LazyRefs. - */ - class CheckNonCyclicMap(sym: Symbol, reportErrors: Boolean)(implicit ctx: Context) extends TypeMap { - - /** Are cycles allowed within nested refinedInfos of currently checked type? */ - private var nestedCycleOK = false - - /** Are cycles allowed within currently checked type? */ - private var cycleOK = false - - /** A diagnostic output string that indicates the position of the last - * part of a type bounds checked by checkInfo. Possible choices: - * alias, lower bound, upper bound. - */ - var where: String = "" - - /** The last type top-level type checked when a CyclicReference occurs. */ - var lastChecked: Type = NoType - - /** Check info `tp` for cycles. Throw CyclicReference for illegal cycles, - * break direct cycle with a LazyRef for legal, F-bounded cycles. - */ - def checkInfo(tp: Type): Type = tp match { - case tp @ TypeAlias(alias) => - try tp.derivedTypeAlias(apply(alias)) - finally { - where = "alias" - lastChecked = alias - } - case tp @ TypeBounds(lo, hi) => - val lo1 = try apply(lo) finally { - where = "lower bound" - lastChecked = lo - } - val saved = nestedCycleOK - nestedCycleOK = true - try tp.derivedTypeBounds(lo1, apply(hi)) - finally { - nestedCycleOK = saved - where = "upper bound" - lastChecked = hi - } - case _ => - tp - } - - private def apply(tp: Type, cycleOK: Boolean, nestedCycleOK: Boolean): Type = { - val savedCycleOK = this.cycleOK - val savedNestedCycleOK = this.nestedCycleOK - this.cycleOK = cycleOK - this.nestedCycleOK = nestedCycleOK - try apply(tp) - finally { - this.cycleOK = savedCycleOK - this.nestedCycleOK = savedNestedCycleOK - } - } - - def apply(tp: Type): Type = tp match { - case tp: TermRef => - this(tp.info) - mapOver(tp) - case tp @ RefinedType(parent, name, rinfo) => - tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK)) - case tp: RecType => - tp.rebind(this(tp.parent)) - case tp @ HKApply(tycon, args) => - tp.derivedAppliedType(this(tycon), args.map(this(_, nestedCycleOK, nestedCycleOK))) - case tp @ TypeRef(pre, name) => - try { - // A prefix is interesting if it might contain (transitively) a reference - // to symbol `sym` itself. We only check references with interesting - // prefixes for cycles. This pruning is done in order not to force - // global symbols when doing the cyclicity check. - def isInteresting(prefix: Type): Boolean = prefix.stripTypeVar match { - case NoPrefix => true - case prefix: ThisType => sym.owner.isClass && prefix.cls.isContainedIn(sym.owner) - case prefix: NamedType => !prefix.symbol.isStaticOwner && isInteresting(prefix.prefix) - case SuperType(thistp, _) => isInteresting(thistp) - case AndType(tp1, tp2) => isInteresting(tp1) || isInteresting(tp2) - case OrType(tp1, tp2) => isInteresting(tp1) && isInteresting(tp2) - case _: RefinedOrRecType | _: HKApply => true - case _ => false - } - if (isInteresting(pre)) { - val pre1 = this(pre, false, false) - checkInfo(tp.info) - if (pre1 eq pre) tp else tp.newLikeThis(pre1) - } - else tp - } catch { - case ex: CyclicReference => - ctx.debuglog(i"cycle detected for $tp, $nestedCycleOK, $cycleOK") - if (cycleOK) LazyRef(() => tp) - else if (reportErrors) throw ex - else tp - } - case _ => mapOver(tp) - } - } - - /** Check that `info` of symbol `sym` is not cyclic. - * @pre sym is not yet initialized (i.e. its type is a Completer). - * @return `info` where every legal F-bounded reference is proctected - * by a `LazyRef`, or `ErrorType` if a cycle was detected and reported. - */ - def checkNonCyclic(sym: Symbol, info: Type, reportErrors: Boolean)(implicit ctx: Context): Type = { - val checker = new CheckNonCyclicMap(sym, reportErrors)(ctx.addMode(Mode.CheckCyclic)) - try checker.checkInfo(info) - catch { - case ex: CyclicReference => - if (reportErrors) { - ctx.error(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos) - ErrorType - } - else info - } - } - - /** Check that refinement satisfies the following two conditions - * 1. No part of it refers to a symbol that's defined in the same refinement - * at a textually later point. - * 2. All references to the refinement itself via `this` are followed by - * selections. - * Note: It's not yet clear what exactly we want to allow and what we want to rule out. - * This depends also on firming up the DOT calculus. For the moment we only issue - * deprecated warnings, not errors. - */ - def checkRefinementNonCyclic(refinement: Tree, refineCls: ClassSymbol, seen: mutable.Set[Symbol]) - (implicit ctx: Context): Unit = { - def flag(what: String, tree: Tree) = - ctx.deprecationWarning(i"$what reference in refinement is deprecated", tree.pos) - def forwardRef(tree: Tree) = flag("forward", tree) - def selfRef(tree: Tree) = flag("self", tree) - val checkTree = new TreeAccumulator[Unit] { - def checkRef(tree: Tree, sym: Symbol) = - if (sym.maybeOwner == refineCls && !seen(sym)) forwardRef(tree) - def apply(x: Unit, tree: Tree)(implicit ctx: Context) = tree match { - case tree: MemberDef => - foldOver(x, tree) - seen += tree.symbol - case tree @ Select(This(_), _) => - checkRef(tree, tree.symbol) - case tree: RefTree => - checkRef(tree, tree.symbol) - foldOver(x, tree) - case tree: This => - selfRef(tree) - case tree: TypeTree => - val checkType = new TypeAccumulator[Unit] { - def apply(x: Unit, tp: Type): Unit = tp match { - case tp: NamedType => - checkRef(tree, tp.symbol) - tp.prefix match { - case pre: ThisType => - case pre => foldOver(x, pre) - } - case tp: ThisType if tp.cls == refineCls => - selfRef(tree) - case _ => - foldOver(x, tp) - } - } - checkType((), tree.tpe) - case _ => - foldOver(x, tree) - } - } - checkTree((), refinement) - } - - /** Check that symbol's definition is well-formed. */ - def checkWellFormed(sym: Symbol)(implicit ctx: Context): Unit = { - //println(i"check wf $sym with flags ${sym.flags}") - def fail(msg: String) = ctx.error(msg, sym.pos) - def varNote = - if (sym.is(Mutable)) "\n(Note that variables need to be initialized to be defined)" - else "" - - def checkWithDeferred(flag: FlagSet) = - if (sym.is(flag)) - fail(i"abstract member may not have `$flag' modifier") - def checkNoConflict(flag1: FlagSet, flag2: FlagSet) = - if (sym.is(allOf(flag1, flag2))) - fail(i"illegal combination of modifiers: $flag1 and $flag2 for: $sym") - - if (sym.is(ImplicitCommon)) { - if (sym.owner.is(Package)) - fail(i"`implicit' modifier cannot be used for top-level definitions") - if (sym.isType) - fail(i"`implicit' modifier cannot be used for types or traits") - } - if (!sym.isClass && sym.is(Abstract)) - fail(i"`abstract' modifier can be used only for classes; it should be omitted for abstract members") - if (sym.is(AbsOverride) && !sym.owner.is(Trait)) - fail(i"`abstract override' modifier only allowed for members of traits") - if (sym.is(Trait) && sym.is(Final)) - fail(i"$sym may not be `final'") - if (sym.hasAnnotation(defn.NativeAnnot)) { - if (!sym.is(Deferred)) - fail(i"`@native' members may not have implementation") - } - else if (sym.is(Deferred, butNot = Param) && !sym.isSelfSym) { - if (!sym.owner.isClass || sym.owner.is(Module) || sym.owner.isAnonymousClass) - fail(i"only classes can have declared but undefined members$varNote") - checkWithDeferred(Private) - checkWithDeferred(Final) - checkWithDeferred(Inline) - } - if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass) - fail(i"$sym cannot extend AnyVal") - checkNoConflict(Final, Sealed) - checkNoConflict(Private, Protected) - checkNoConflict(Abstract, Override) - } - - /** Check the type signature of the symbol `M` defined by `tree` does not refer - * to a private type or value which is invisible at a point where `M` is still - * visible. As an exception, we allow references to type aliases if the underlying - * type of the alias is not a leak. So type aliases are transparent as far as - * leak testing is concerned. - * @return The `info` of `sym`, with problematic aliases expanded away. - * See i997.scala for tests, i1130.scala for a case where it matters that we - * transform leaky aliases away. - */ - def checkNoPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = { - class NotPrivate extends TypeMap { - type Errors = List[(String, Position)] - var errors: Errors = Nil - def accessBoundary(sym: Symbol): Symbol = - if (sym.is(Private)) sym.owner - else if (sym.privateWithin.exists) sym.privateWithin - else if (sym.is(Package)) sym - else accessBoundary(sym.owner) - def apply(tp: Type): Type = tp match { - case tp: NamedType => - val prevErrors = errors - var tp1 = - if (tp.symbol.is(Private) && - !accessBoundary(sym).isContainedIn(tp.symbol.owner)) { - errors = (em"non-private $sym refers to private ${tp.symbol}\n in its type signature ${sym.info}", - sym.pos) :: errors - tp - } - else mapOver(tp) - if ((errors ne prevErrors) && tp.info.isAlias) { - // try to dealias to avoid a leak error - val savedErrors = errors - errors = prevErrors - val tp2 = apply(tp.superType) - if (errors eq prevErrors) tp1 = tp2 - else errors = savedErrors - } - tp1 - case tp: ClassInfo => - tp.derivedClassInfo( - prefix = apply(tp.prefix), - classParents = tp.parentsWithArgs.map(p => - apply(p).underlyingClassRef(refinementOK = false).asInstanceOf[TypeRef])) - case _ => - mapOver(tp) - } - } - val notPrivate = new NotPrivate - val info = notPrivate(sym.info) - notPrivate.errors.foreach { case (msg, pos) => ctx.errorOrMigrationWarning(msg, pos) } - info - } -} - -trait Checking { - - import tpd._ - - def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = - Checking.checkNonCyclic(sym, info, reportErrors) - - /** Check that Java statics and packages can only be used in selections. - */ - def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = { - if (!proto.isInstanceOf[SelectionProto]) { - val sym = tree.tpe.termSymbol - // The check is avoided inside Java compilation units because it always fails - // on the singleton type Module.type. - if ((sym is Package) || ((sym is JavaModule) && !ctx.compilationUnit.isJava)) ctx.error(em"$sym is not a value", tree.pos) - } - tree - } - - /** Check that type `tp` is stable. */ - def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = - if (!tp.isStable) ctx.error(ex"$tp is not stable", pos) - - /** Check that all type members of `tp` have realizable bounds */ - def checkRealizableBounds(tp: Type, pos: Position)(implicit ctx: Context): Unit = { - val rstatus = boundsRealizability(tp) - if (rstatus ne Realizable) - ctx.error(ex"$tp cannot be instantiated since it${rstatus.msg}", pos) - } - - /** Check that `tp` is a class type. - * Also, if `traitReq` is true, check that `tp` is a trait. - * Also, if `stablePrefixReq` is true and phase is not after RefChecks, - * check that class prefix is stable. - * @return `tp` itself if it is a class or trait ref, ObjectType if not. - */ - def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = - tp.underlyingClassRef(refinementOK = false) match { - case tref: TypeRef => - if (traitReq && !(tref.symbol is Trait)) ctx.error(ex"$tref is not a trait", pos) - if (stablePrefixReq && ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos) - tp - case _ => - ctx.error(ex"$tp is not a class type", pos) - defn.ObjectType - } - - /** Check that a non-implicit parameter making up the first parameter section of an - * implicit conversion is not a singleton type. - */ - def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match { - case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) => - if (vparam.tpt.tpe.isInstanceOf[SingletonType]) - ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos) - case _ => - } - - /** Check that any top-level type arguments in this type are feasible, i.e. that - * their lower bound conforms to their upper bound. If a type argument is - * infeasible, issue and error and continue with upper bound. - */ - def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { - case tp: RefinedType => - tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) - case tp: RecType => - tp.rebind(tp.parent) - case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => - ctx.error(ex"no type exists between low bound $lo and high bound $hi$where", pos) - TypeAlias(hi) - case _ => - tp - } - - /** Check that `tree` is a pure expression of constant type */ - def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context): Unit = - tree.tpe match { - case tp: TermRef if tp.symbol.is(InlineParam) => // ok - case tp => tp.widenTermRefExpr match { - case tp: ConstantType if isPureExpr(tree) => // ok - case tp if defn.isFunctionType(tp) && isPureExpr(tree) => // ok - case _ => ctx.error(em"$what must be a constant expression or a function", tree.pos) - } - } - - /** Check that class does not define same symbol twice */ - def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { - val seen = new mutable.HashMap[Name, List[Symbol]] { - override def default(key: Name) = Nil - } - typr.println(i"check no double defs $cls") - - def checkDecl(decl: Symbol): Unit = { - for (other <- seen(decl.name)) { - typr.println(i"conflict? $decl $other") - if (decl.matches(other)) { - def doubleDefError(decl: Symbol, other: Symbol): Unit = { - def ofType = if (decl.isType) "" else em": ${other.info}" - def explanation = - if (!decl.isRealMethod) "" - else "\n (the definitions have matching type signatures)" - ctx.error(em"$decl is already defined as $other$ofType$explanation", decl.pos) - } - if (decl is Synthetic) doubleDefError(other, decl) - else doubleDefError(decl, other) - } - if ((decl is HasDefaultParams) && (other is HasDefaultParams)) { - ctx.error(em"two or more overloaded variants of $decl have default arguments") - decl resetFlag HasDefaultParams - } - } - seen(decl.name) = decl :: seen(decl.name) - } - - cls.info.decls.foreach(checkDecl) - cls.info match { - case ClassInfo(_, _, _, _, selfSym: Symbol) => checkDecl(selfSym) - case _ => - } - } - - def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = - if (!ctx.isAfterTyper) { - val called = call.tpe.classSymbol - if (caller is Trait) - ctx.error(i"$caller may not call constructor of $called", call.pos) - else if (called.is(Trait) && !caller.mixins.contains(called)) - ctx.error(i"""$called is already implemented by super${caller.superClass}, - |its constructor cannot be called again""", call.pos) - } - - /** Check that `tpt` does not define a higher-kinded type */ - def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = - if (tpt.tpe.isHK && !ctx.compilationUnit.isJava) { - // be more lenient with missing type params in Java, - // needed to make pos/java-interop/t1196 work. - errorTree(tpt, ex"missing type parameter for ${tpt.tpe}") - } - else tpt - - /** Check that `tpt` does not refer to a singleton type */ - def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = - if (tpt.tpe.isInstanceOf[SingletonType]) { - errorTree(tpt, ex"Singleton type ${tpt.tpe} is not allowed $where") - } - else tpt -} - -trait NoChecking extends Checking { - import tpd._ - override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info - override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree - override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () - override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp - override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () - override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp - override def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context) = () - override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = () - override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = () - override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt - override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt -} diff --git a/src/dotty/tools/dotc/typer/ConstFold.scala b/src/dotty/tools/dotc/typer/ConstFold.scala deleted file mode 100644 index 68a5d05f5..000000000 --- a/src/dotty/tools/dotc/typer/ConstFold.scala +++ /dev/null @@ -1,182 +0,0 @@ -package dotty.tools.dotc -package typer - -import java.lang.ArithmeticException - -import ast._ -import Trees._ -import core._ -import Types._ -import Constants._ -import Names._ -import StdNames._ -import Contexts._ - -object ConstFold { - - import tpd._ - - /** If tree is a constant operation, replace with result. */ - def apply(tree: Tree)(implicit ctx: Context): Tree = finish(tree) { - tree match { - case Apply(Select(xt, op), yt :: Nil) => - xt.tpe.widenTermRefExpr match { - case ConstantType(x) => - yt.tpe.widenTermRefExpr match { - case ConstantType(y) => foldBinop(op, x, y) - case _ => null - } - case _ => null - } - case Select(xt, op) => - xt.tpe.widenTermRefExpr match { - case ConstantType(x) => foldUnop(op, x) - case _ => null - } - case _ => null - } - } - - /** If tree is a constant value that can be converted to type `pt`, perform - * the conversion. - */ - def apply(tree: Tree, pt: Type)(implicit ctx: Context): Tree = - finish(apply(tree)) { - tree.tpe.widenTermRefExpr match { - case ConstantType(x) => x convertTo pt - case _ => null - } - } - - private def finish(tree: Tree)(compX: => Constant)(implicit ctx: Context): Tree = - try { - val x = compX - if (x ne null) tree withType ConstantType(x) - else tree - } catch { - case _: ArithmeticException => tree // the code will crash at runtime, - // but that is better than the - // compiler itself crashing - } - - private def foldUnop(op: Name, x: Constant): Constant = (op, x.tag) match { - case (nme.UNARY_!, BooleanTag) => Constant(!x.booleanValue) - - case (nme.UNARY_~ , IntTag ) => Constant(~x.intValue) - case (nme.UNARY_~ , LongTag ) => Constant(~x.longValue) - - case (nme.UNARY_+ , IntTag ) => Constant(x.intValue) - case (nme.UNARY_+ , LongTag ) => Constant(x.longValue) - case (nme.UNARY_+ , FloatTag ) => Constant(x.floatValue) - case (nme.UNARY_+ , DoubleTag ) => Constant(x.doubleValue) - - case (nme.UNARY_- , IntTag ) => Constant(-x.intValue) - case (nme.UNARY_- , LongTag ) => Constant(-x.longValue) - case (nme.UNARY_- , FloatTag ) => Constant(-x.floatValue) - case (nme.UNARY_- , DoubleTag ) => Constant(-x.doubleValue) - - case _ => null - } - - /** These are local helpers to keep foldBinop from overly taxing the - * optimizer. - */ - private def foldBooleanOp(op: Name, x: Constant, y: Constant): Constant = op match { - case nme.ZOR => Constant(x.booleanValue | y.booleanValue) - case nme.OR => Constant(x.booleanValue | y.booleanValue) - case nme.XOR => Constant(x.booleanValue ^ y.booleanValue) - case nme.ZAND => Constant(x.booleanValue & y.booleanValue) - case nme.AND => Constant(x.booleanValue & y.booleanValue) - case nme.EQ => Constant(x.booleanValue == y.booleanValue) - case nme.NE => Constant(x.booleanValue != y.booleanValue) - case _ => null - } - private def foldSubrangeOp(op: Name, x: Constant, y: Constant): Constant = op match { - case nme.OR => Constant(x.intValue | y.intValue) - case nme.XOR => Constant(x.intValue ^ y.intValue) - case nme.AND => Constant(x.intValue & y.intValue) - case nme.LSL => Constant(x.intValue << y.intValue) - case nme.LSR => Constant(x.intValue >>> y.intValue) - case nme.ASR => Constant(x.intValue >> y.intValue) - case nme.EQ => Constant(x.intValue == y.intValue) - case nme.NE => Constant(x.intValue != y.intValue) - case nme.LT => Constant(x.intValue < y.intValue) - case nme.GT => Constant(x.intValue > y.intValue) - case nme.LE => Constant(x.intValue <= y.intValue) - case nme.GE => Constant(x.intValue >= y.intValue) - case nme.ADD => Constant(x.intValue + y.intValue) - case nme.SUB => Constant(x.intValue - y.intValue) - case nme.MUL => Constant(x.intValue * y.intValue) - case nme.DIV => Constant(x.intValue / y.intValue) - case nme.MOD => Constant(x.intValue % y.intValue) - case _ => null - } - private def foldLongOp(op: Name, x: Constant, y: Constant): Constant = op match { - case nme.OR => Constant(x.longValue | y.longValue) - case nme.XOR => Constant(x.longValue ^ y.longValue) - case nme.AND => Constant(x.longValue & y.longValue) - case nme.LSL => Constant(x.longValue << y.longValue) - case nme.LSR => Constant(x.longValue >>> y.longValue) - case nme.ASR => Constant(x.longValue >> y.longValue) - case nme.EQ => Constant(x.longValue == y.longValue) - case nme.NE => Constant(x.longValue != y.longValue) - case nme.LT => Constant(x.longValue < y.longValue) - case nme.GT => Constant(x.longValue > y.longValue) - case nme.LE => Constant(x.longValue <= y.longValue) - case nme.GE => Constant(x.longValue >= y.longValue) - case nme.ADD => Constant(x.longValue + y.longValue) - case nme.SUB => Constant(x.longValue - y.longValue) - case nme.MUL => Constant(x.longValue * y.longValue) - case nme.DIV => Constant(x.longValue / y.longValue) - case nme.MOD => Constant(x.longValue % y.longValue) - case _ => null - } - private def foldFloatOp(op: Name, x: Constant, y: Constant): Constant = op match { - case nme.EQ => Constant(x.floatValue == y.floatValue) - case nme.NE => Constant(x.floatValue != y.floatValue) - case nme.LT => Constant(x.floatValue < y.floatValue) - case nme.GT => Constant(x.floatValue > y.floatValue) - case nme.LE => Constant(x.floatValue <= y.floatValue) - case nme.GE => Constant(x.floatValue >= y.floatValue) - case nme.ADD => Constant(x.floatValue + y.floatValue) - case nme.SUB => Constant(x.floatValue - y.floatValue) - case nme.MUL => Constant(x.floatValue * y.floatValue) - case nme.DIV => Constant(x.floatValue / y.floatValue) - case nme.MOD => Constant(x.floatValue % y.floatValue) - case _ => null - } - private def foldDoubleOp(op: Name, x: Constant, y: Constant): Constant = op match { - case nme.EQ => Constant(x.doubleValue == y.doubleValue) - case nme.NE => Constant(x.doubleValue != y.doubleValue) - case nme.LT => Constant(x.doubleValue < y.doubleValue) - case nme.GT => Constant(x.doubleValue > y.doubleValue) - case nme.LE => Constant(x.doubleValue <= y.doubleValue) - case nme.GE => Constant(x.doubleValue >= y.doubleValue) - case nme.ADD => Constant(x.doubleValue + y.doubleValue) - case nme.SUB => Constant(x.doubleValue - y.doubleValue) - case nme.MUL => Constant(x.doubleValue * y.doubleValue) - case nme.DIV => Constant(x.doubleValue / y.doubleValue) - case nme.MOD => Constant(x.doubleValue % y.doubleValue) - case _ => null - } - - private def foldBinop(op: Name, x: Constant, y: Constant): Constant = { - val optag = - if (x.tag == y.tag) x.tag - else if (x.isNumeric && y.isNumeric) math.max(x.tag, y.tag) - else NoTag - - try optag match { - case BooleanTag => foldBooleanOp(op, x, y) - case ByteTag | ShortTag | CharTag | IntTag => foldSubrangeOp(op, x, y) - case LongTag => foldLongOp(op, x, y) - case FloatTag => foldFloatOp(op, x, y) - case DoubleTag => foldDoubleOp(op, x, y) - case StringTag if op == nme.ADD => Constant(x.stringValue + y.stringValue) - case _ => null - } - catch { - case ex: ArithmeticException => null - } - } -} diff --git a/src/dotty/tools/dotc/typer/Docstrings.scala b/src/dotty/tools/dotc/typer/Docstrings.scala deleted file mode 100644 index 370844e65..000000000 --- a/src/dotty/tools/dotc/typer/Docstrings.scala +++ /dev/null @@ -1,56 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import Contexts._, Symbols._, Decorators._, Comments._ -import util.Positions._ -import ast.tpd - -trait Docstrings { self: Typer => - - /** The Docstrings typer will handle the expansion of `@define` and - * `@inheritdoc` if there is a `DocContext` present as a property in the - * supplied `ctx`. - * - * It will also type any `@usecase` available in function definitions. - */ - def cookComments(syms: List[Symbol], owner: Symbol)(implicit ctx: Context): Unit = - ctx.docCtx.foreach { docbase => - val relevantSyms = syms.filter(docbase.docstring(_).isDefined) - relevantSyms.foreach { sym => - expandParentDocs(sym) - val usecases = docbase.docstring(sym).map(_.usecases).getOrElse(Nil) - - usecases.foreach { usecase => - enterSymbol(createSymbol(usecase.untpdCode)) - - typedStats(usecase.untpdCode :: Nil, owner) match { - case List(df: tpd.DefDef) => usecase.tpdCode = df - case _ => ctx.error("`@usecase` was not a valid definition", usecase.codePos) - } - } - } - } - - private def expandParentDocs(sym: Symbol)(implicit ctx: Context): Unit = - ctx.docCtx.foreach { docCtx => - docCtx.docstring(sym).foreach { cmt => - def expandDoc(owner: Symbol): Unit = if (!cmt.isExpanded) { - val tplExp = docCtx.templateExpander - tplExp.defineVariables(sym) - - val newCmt = cmt - .expand(tplExp.expandedDocComment(sym, owner, _)) - .withUsecases - - docCtx.addDocstring(sym, Some(newCmt)) - } - - if (sym ne NoSymbol) { - expandParentDocs(sym.owner) - expandDoc(sym.owner) - } - } - } -} diff --git a/src/dotty/tools/dotc/typer/Dynamic.scala b/src/dotty/tools/dotc/typer/Dynamic.scala deleted file mode 100644 index b5ace87d3..000000000 --- a/src/dotty/tools/dotc/typer/Dynamic.scala +++ /dev/null @@ -1,104 +0,0 @@ -package dotty.tools -package dotc -package typer - -import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.ast.untpd -import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Names.Name -import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.core.Decorators._ - -object Dynamic { - def isDynamicMethod(name: Name): Boolean = - name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed -} - -/** Translates selection that does not typecheck according to the scala.Dynamic rules: - * foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux) - * foo.bar = baz ~~> foo.updateDynamic("bar")(baz) - * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...) - * foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...) - * foo.bar ~~> foo.selectDynamic(bar) - * - * The first matching rule of is applied. - */ -trait Dynamic { self: Typer with Applications => - import Dynamic._ - import tpd._ - - /** Translate selection that does not typecheck according to the normal rules into a applyDynamic/applyDynamicNamed. - * foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...) - * foo.bar[T0, ...](baz0, baz1, ...) ~~> foo.applyDynamic[T0, ...](bar)(baz0, baz1, ...) - * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...) - * foo.bar[T0, ...](x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed[T0, ...]("bar")(("x", bazX), ("y", bazY), ("", baz), ...) - */ - def typedDynamicApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { - def typedDynamicApply(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree = { - def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false } - val args = tree.args - val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic - if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) { - ctx.error("applyDynamicNamed does not support passing a vararg parameter", tree.pos) - tree.withType(ErrorType) - } else { - def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg)) - def namedArgs = args.map { - case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg) - case arg => namedArgTuple("", arg) - } - val args1 = if (dynName == nme.applyDynamic) args else namedArgs - typedApply(untpd.Apply(coreDynamic(qual, dynName, name, targs), args1), pt) - } - } - - tree.fun match { - case Select(qual, name) if !isDynamicMethod(name) => - typedDynamicApply(qual, name, Nil) - case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) => - typedDynamicApply(qual, name, targs) - case TypeApply(fun, targs) => - typedDynamicApply(fun, nme.apply, targs) - case fun => - typedDynamicApply(fun, nme.apply, Nil) - } - } - - /** Translate selection that does not typecheck according to the normal rules into a selectDynamic. - * foo.bar ~~> foo.selectDynamic(bar) - * foo.bar[T0, ...] ~~> foo.selectDynamic[T0, ...](bar) - * - * Note: inner part of translation foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux) is achieved - * through an existing transformation of in typedAssign [foo.bar(baz) = quux ~~> foo.bar.update(baz, quux)]. - */ - def typedDynamicSelect(tree: untpd.Select, targs: List[Tree], pt: Type)(implicit ctx: Context): Tree = - typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name, targs), pt) - - /** Translate selection that does not typecheck according to the normal rules into a updateDynamic. - * foo.bar = baz ~~> foo.updateDynamic(bar)(baz) - */ - def typedDynamicAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree = { - def typedDynamicAssign(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree = - typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, targs), tree.rhs), pt) - tree.lhs match { - case Select(qual, name) if !isDynamicMethod(name) => - typedDynamicAssign(qual, name, Nil) - case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) => - typedDynamicAssign(qual, name, targs) - case _ => - ctx.error("reassignment to val", tree.pos) - tree.withType(ErrorType) - } - } - - private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, targs: List[untpd.Tree])(implicit ctx: Context): untpd.Apply = { - val select = untpd.Select(qual, dynName) - val selectWithTypes = - if (targs.isEmpty) select - else untpd.TypeApply(select, targs) - untpd.Apply(selectWithTypes, Literal(Constant(name.toString))) - } -} diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala deleted file mode 100644 index a18c83ff8..000000000 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ /dev/null @@ -1,153 +0,0 @@ -package dotty.tools -package dotc -package typer - -import ast._ -import core._ -import Trees._ -import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._ -import Applications._, Implicits._, Flags._ -import util.Positions._ -import printing.{Showable, RefinedPrinter} -import scala.collection.mutable -import java.util.regex.Matcher.quoteReplacement -import reporting.diagnostic.Message -import reporting.diagnostic.messages._ - -object ErrorReporting { - - import tpd._ - - def errorTree(tree: untpd.Tree, msg: => Message)(implicit ctx: Context): tpd.Tree = - tree withType errorType(msg, tree.pos) - - def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = { - ctx.error(msg, pos) - ErrorType - } - - def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = { - val cycleSym = ex.denot.symbol - def errorMsg(msg: String, cx: Context): String = - if (cx.mode is Mode.InferringReturnType) { - cx.tree match { - case tree: untpd.ValOrDefDef => - // Dotty deviation: Was Trees.ValOrDefDef[_], but this gives ValOrDefDef[Nothing] instead of - // ValOrDefDel[Null]. Scala handles it, but it looks accidental because bounds propagation - // fails if the parameter is invariant or cotravariant. - // See test pending/pos/boundspropagation.scala - val treeSym = ctx.symOfContextTree(tree) - if (treeSym.exists && treeSym.name == cycleSym.name && treeSym.owner == cycleSym.owner) { - val result = if (cycleSym is Method) " result" else "" - em"overloaded or recursive $cycleSym needs$result type" - } - else errorMsg(msg, cx.outer) - case _ => - errorMsg(msg, cx.outer) - } - } else msg - errorMsg(ex.show, ctx) - } - - def wrongNumberOfArgs(fntpe: Type, kind: String, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) = - errorType(WrongNumberOfArgs(fntpe, kind, expectedArgs, actual)(ctx), pos) - - class Errors(implicit ctx: Context) { - - /** An explanatory note to be added to error messages - * when there's a problem with abstract var defs */ - def abstractVarMessage(sym: Symbol): String = - if (sym.underlyingSymbol.is(Mutable)) - "\n(Note that variables need to be initialized to be defined)" - else "" - - def expectedTypeStr(tp: Type): String = tp match { - case tp: PolyProto => - em"type arguments [${tp.targs}%, %] and ${expectedTypeStr(tp.resultType)}" - case tp: FunProto => - val result = tp.resultType match { - case _: WildcardType | _: IgnoredProto => "" - case tp => em" and expected result type $tp" - } - em"arguments (${tp.typedArgs.tpes}%, %)$result" - case _ => - em"expected type $tp" - } - - def anonymousTypeMemberStr(tpe: Type) = { - val kind = tpe match { - case _: TypeBounds => "type with bounds" - case _: PolyType | _: MethodType => "method" - case _ => "value of type" - } - em"$kind $tpe" - } - - def overloadedAltsStr(alts: List[SingleDenotation]) = - em"overloaded alternatives of ${denotStr(alts.head)} with types\n" + - em" ${alts map (_.info)}%\n %" - - def denotStr(denot: Denotation): String = - if (denot.isOverloaded) overloadedAltsStr(denot.alternatives) - else if (denot.symbol.exists) denot.symbol.showLocated - else anonymousTypeMemberStr(denot.info) - - def refStr(tp: Type): String = tp match { - case tp: NamedType => denotStr(tp.denot) - case _ => anonymousTypeMemberStr(tp) - } - - def exprStr(tree: Tree): String = refStr(tree.tpe) - - def patternConstrStr(tree: Tree): String = ??? - - def typeMismatch(tree: Tree, pt: Type, implicitFailure: SearchFailure = NoImplicitMatches): Tree = - errorTree(tree, typeMismatchMsg(normalize(tree.tpe, pt), pt, implicitFailure.postscript)) - - /** A subtype log explaining why `found` does not conform to `expected` */ - def whyNoMatchStr(found: Type, expected: Type) = - if (ctx.settings.explaintypes.value) - "\n" + ctx.typerState.show + "\n" + TypeComparer.explained((found <:< expected)(_)) - else - "" - - def typeMismatchMsg(found: Type, expected: Type, postScript: String = "") = { - // replace constrained polyparams and their typevars by their bounds where possible - object reported extends TypeMap { - def setVariance(v: Int) = variance = v - val constraint = ctx.typerState.constraint - def apply(tp: Type): Type = tp match { - case tp: PolyParam => - constraint.entry(tp) match { - case bounds: TypeBounds => - if (variance < 0) apply(constraint.fullUpperBound(tp)) - else if (variance > 0) apply(constraint.fullLowerBound(tp)) - else tp - case NoType => tp - case instType => apply(instType) - } - case tp: TypeVar => apply(tp.stripTypeVar) - case _ => mapOver(tp) - } - } - val found1 = reported(found) - reported.setVariance(-1) - val expected1 = reported(expected) - TypeMismatch(found1, expected1, whyNoMatchStr(found, expected), postScript) - } - - /** Format `raw` implicitNotFound argument, replacing all - * occurrences of `${X}` where `X` is in `paramNames` with the - * corresponding shown type in `args`. - */ - def implicitNotFoundString(raw: String, paramNames: List[String], args: List[Type]): String = { - def translate(name: String): Option[String] = { - val idx = paramNames.indexOf(name) - if (idx >= 0) Some(quoteReplacement(ex"${args(idx)}")) else None - } - """\$\{\w*\}""".r.replaceSomeIn(raw, m => translate(m.matched.drop(2).init)) - } - } - - def err(implicit ctx: Context): Errors = new Errors -} diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala deleted file mode 100644 index c390ae808..000000000 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ /dev/null @@ -1,191 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast.{Trees, untpd, tpd, TreeInfo} -import Contexts._ -import Types._ -import Flags._ -import NameOps._ -import Symbols._ -import Decorators._ -import Names._ -import StdNames._ -import Trees._ -import Inferencing._ -import util.Positions._ -import collection.mutable - -object EtaExpansion { - - import tpd._ - - private def lift(defs: mutable.ListBuffer[Tree], expr: Tree, prefix: String = "")(implicit ctx: Context): Tree = - if (isPureExpr(expr)) expr - else { - val name = ctx.freshName(prefix).toTermName - val liftedType = fullyDefinedType(expr.tpe.widen, "lifted expression", expr.pos) - val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, liftedType, coord = positionCoord(expr.pos)) - defs += ValDef(sym, expr) - ref(sym.valRef) - } - - /** Lift out common part of lhs tree taking part in an operator assignment such as - * - * lhs += expr - */ - def liftAssigned(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { - case Apply(MaybePoly(fn @ Select(pre, name), targs), args) => - cpy.Apply(tree)( - cpy.Select(fn)( - lift(defs, pre), name).appliedToTypeTrees(targs), - liftArgs(defs, fn.tpe, args)) - case Select(pre, name) => - cpy.Select(tree)(lift(defs, pre), name) - case _ => - tree - } - - /** Lift a function argument, stripping any NamedArg wrapper */ - def liftArg(defs: mutable.ListBuffer[Tree], arg: Tree, prefix: String = "")(implicit ctx: Context): Tree = - arg match { - case arg @ NamedArg(name, arg1) => cpy.NamedArg(arg)(name, lift(defs, arg1, prefix)) - case arg => lift(defs, arg, prefix) - } - - /** Lift arguments that are not-idempotent into ValDefs in buffer `defs` - * and replace by the idents of so created ValDefs. - */ - def liftArgs(defs: mutable.ListBuffer[Tree], methRef: Type, args: List[Tree])(implicit ctx: Context) = - methRef.widen match { - case MethodType(paramNames, paramTypes) => - (args, paramNames, paramTypes).zipped map { (arg, name, tp) => - if (tp.isInstanceOf[ExprType]) arg - else liftArg(defs, arg, if (name contains '$') "" else name.toString + "$") - } - case _ => - args map (liftArg(defs, _)) - } - - /** Lift out function prefix and all arguments from application - * - * pre.f(arg1, ..., argN) becomes - * - * val x0 = pre - * val x1 = arg1 - * ... - * val xN = argN - * x0.f(x1, ..., xN) - * - * But leave idempotent expressions alone. - * - */ - def liftApp(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { - case Apply(fn, args) => - cpy.Apply(tree)(liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) - case TypeApply(fn, targs) => - cpy.TypeApply(tree)(liftApp(defs, fn), targs) - case Select(pre, name) if isPureRef(tree) => - cpy.Select(tree)(liftPrefix(defs, pre), name) - case Block(stats, expr) => - liftApp(defs ++= stats, expr) - case New(tpt) => - tree - case _ => - lift(defs, tree) - } - - /** Lift prefix `pre` of an application `pre.f(...)` to - * - * val x0 = pre - * x0.f(...) - * - * unless `pre` is a `New` or `pre` is idempotent. - */ - def liftPrefix(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { - case New(_) => tree - case _ => if (isIdempotentExpr(tree)) tree else lift(defs, tree) - } - - /** Eta-expanding a tree means converting a method reference to a function value. - * @param tree The tree to expand - * @param mt The type of the method reference - * @param xarity The arity of the expected function type - * and assume the lifted application of `tree` (@see liftApp) is - * - * { val xs = es; expr } - * - * If xarity matches the number of parameters in `mt`, the eta-expansion is - * - * { val xs = es; (x1, ..., xn) => expr(x1, ..., xn) } - * - * Note that the function value's parameters are untyped, hence the type will - * be supplied by the environment (or if missing be supplied by the target - * method as a fallback). On the other hand, if `xarity` is different from - * the number of parameters in `mt`, then we cannot propagate parameter types - * from the expected type, and we fallback to using the method's original - * parameter types instead. - * - * In either case, the result is an untyped tree, with `es` and `expr` as typed splices. - */ - def etaExpand(tree: Tree, mt: MethodType, xarity: Int)(implicit ctx: Context): untpd.Tree = { - import untpd._ - assert(!ctx.isAfterTyper) - val defs = new mutable.ListBuffer[tpd.Tree] - val lifted: Tree = TypedSplice(liftApp(defs, tree)) - val paramTypes: List[Tree] = - if (mt.paramTypes.length == xarity) mt.paramTypes map (_ => TypeTree()) - else mt.paramTypes map TypeTree - val params = (mt.paramNames, paramTypes).zipped.map((name, tpe) => - ValDef(name, tpe, EmptyTree).withFlags(Synthetic | Param).withPos(tree.pos)) - var ids: List[Tree] = mt.paramNames map (name => Ident(name).withPos(tree.pos)) - if (mt.paramTypes.nonEmpty && mt.paramTypes.last.isRepeatedParam) - ids = ids.init :+ repeated(ids.last) - var body: Tree = Apply(lifted, ids) - mt.resultType match { - case rt: MethodType if !rt.isImplicit => body = PostfixOp(body, nme.WILDCARD) - case _ => - } - val fn = untpd.Function(params, body) - if (defs.nonEmpty) untpd.Block(defs.toList map (untpd.TypedSplice(_)), fn) else fn - } -} - - /** <p> not needed - * Expand partial function applications of type `type`. - * </p><pre> - * p.f(es_1)...(es_n) - * ==> { - * <b>private synthetic val</b> eta$f = p.f // if p is not stable - * ... - * <b>private synthetic val</b> eta$e_i = e_i // if e_i is not stable - * ... - * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m)) - * }</pre> - * <p> - * tree is already attributed - * </p> - def etaExpandUntyped(tree: Tree)(implicit ctx: Context): untpd.Tree = { // kept as a reserve for now - def expand(tree: Tree): untpd.Tree = tree.tpe match { - case mt @ MethodType(paramNames, paramTypes) if !mt.isImplicit => - val paramsArgs: List[(untpd.ValDef, untpd.Tree)] = - (paramNames, paramTypes).zipped.map { (name, tp) => - val droppedStarTpe = defn.underlyingOfRepeated(tp) - val param = ValDef( - Modifiers(Param), name, - untpd.TypedSplice(TypeTree(droppedStarTpe)), untpd.EmptyTree) - var arg: untpd.Tree = Ident(name) - if (defn.isRepeatedParam(tp)) - arg = Typed(arg, Ident(tpnme.WILDCARD_STAR)) - (param, arg) - } - val (params, args) = paramsArgs.unzip - untpd.Function(params, Apply(untpd.TypedSplice(tree), args)) - } - - val defs = new mutable.ListBuffer[Tree] - val tree1 = liftApp(defs, tree) - Block(defs.toList map untpd.TypedSplice, expand(tree1)) - } - */ diff --git a/src/dotty/tools/dotc/typer/FrontEnd.scala b/src/dotty/tools/dotc/typer/FrontEnd.scala deleted file mode 100644 index c444631ae..000000000 --- a/src/dotty/tools/dotc/typer/FrontEnd.scala +++ /dev/null @@ -1,83 +0,0 @@ -package dotty.tools.dotc -package typer - -import core._ -import Phases._ -import Contexts._ -import Symbols._ -import dotty.tools.dotc.parsing.JavaParsers.JavaParser -import parsing.Parsers.Parser -import config.Config -import config.Printers.{typr, default} -import util.Stats._ -import scala.util.control.NonFatal -import ast.Trees._ - -class FrontEnd extends Phase { - - override def phaseName = "frontend" - override def isTyper = true - import ast.tpd - - def monitor(doing: String)(body: => Unit)(implicit ctx: Context) = - try body - catch { - case NonFatal(ex) => - ctx.echo(s"exception occurred while $doing ${ctx.compilationUnit}") - throw ex - } - - def parse(implicit ctx: Context) = monitor("parsing") { - val unit = ctx.compilationUnit - unit.untpdTree = - if (unit.isJava) new JavaParser(unit.source).parse() - else new Parser(unit.source).parse() - val printer = if (ctx.settings.Xprint.value.contains("parser")) default else typr - printer.println("parsed:\n" + unit.untpdTree.show) - if (Config.checkPositions) - unit.untpdTree.checkPos(nonOverlapping = !unit.isJava && !ctx.reporter.hasErrors) - } - - def enterSyms(implicit ctx: Context) = monitor("indexing") { - val unit = ctx.compilationUnit - ctx.typer.index(unit.untpdTree) - typr.println("entered: " + unit.source) - } - - def typeCheck(implicit ctx: Context) = monitor("typechecking") { - val unit = ctx.compilationUnit - unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree) - typr.println("typed: " + unit.source) - record("retained untyped trees", unit.untpdTree.treeSize) - record("retained typed trees after typer", unit.tpdTree.treeSize) - } - - private def firstTopLevelDef(trees: List[tpd.Tree])(implicit ctx: Context): Symbol = trees match { - case PackageDef(_, defs) :: _ => firstTopLevelDef(defs) - case Import(_, _) :: defs => firstTopLevelDef(defs) - case (tree @ TypeDef(_, _)) :: _ => tree.symbol - case _ => NoSymbol - } - - protected def discardAfterTyper(unit: CompilationUnit)(implicit ctx: Context) = - unit.isJava || firstTopLevelDef(unit.tpdTree :: Nil).isPrimitiveValueClass - - override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { - val unitContexts = for (unit <- units) yield { - ctx.inform(s"compiling ${unit.source}") - ctx.fresh.setCompilationUnit(unit) - } - unitContexts foreach (parse(_)) - record("parsedTrees", ast.Trees.ntrees) - unitContexts foreach (enterSyms(_)) - unitContexts foreach (typeCheck(_)) - record("total trees after typer", ast.Trees.ntrees) - unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper) - } - - override def run(implicit ctx: Context): Unit = { - parse - enterSyms - typeCheck - } -} diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala deleted file mode 100644 index f3dceea71..000000000 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ /dev/null @@ -1,844 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast.{Trees, untpd, tpd, TreeInfo} -import util.Positions._ -import util.Stats.{track, record, monitored} -import printing.Showable -import Contexts._ -import Types._ -import Flags._ -import TypeErasure.{erasure, hasStableErasure} -import Mode.ImplicitsEnabled -import Denotations._ -import NameOps._ -import SymDenotations._ -import Symbols._ -import Types._ -import Decorators._ -import Names._ -import StdNames._ -import Constants._ -import Applications._ -import ProtoTypes._ -import ErrorReporting._ -import Inferencing.fullyDefinedType -import Trees._ -import Hashable._ -import config.Config -import config.Printers.{implicits, implicitsDetailed} -import collection.mutable - -/** Implicit resolution */ -object Implicits { - - /** A common base class of contextual implicits and of-type implicits which - * represents a set of implicit references. - */ - abstract class ImplicitRefs(initctx: Context) { - implicit val ctx: Context = - if (initctx == NoContext) initctx else initctx retractMode Mode.ImplicitsEnabled - - /** The implicit references */ - def refs: List[TermRef] - - /** Return those references in `refs` that are compatible with type `pt`. */ - protected def filterMatching(pt: Type)(implicit ctx: Context): List[TermRef] = track("filterMatching") { - - def refMatches(ref: TermRef)(implicit ctx: Context) = /*ctx.traceIndented(i"refMatches $ref $pt")*/ { - - def discardForView(tpw: Type, argType: Type): Boolean = tpw match { - case mt: MethodType => - mt.isImplicit || - mt.paramTypes.length != 1 || - !(argType relaxed_<:< mt.paramTypes.head)(ctx.fresh.setExploreTyperState) - case poly: PolyType => - // We do not need to call ProtoTypes#constrained on `poly` because - // `refMatches` is always called with mode TypevarsMissContext enabled. - poly.resultType match { - case mt: MethodType => - mt.isImplicit || - mt.paramTypes.length != 1 || - !(argType relaxed_<:< wildApprox(mt.paramTypes.head)(ctx.fresh.setExploreTyperState)) - case rtp => - discardForView(wildApprox(rtp), argType) - } - case tpw: TermRef => - false // can't discard overloaded refs - case tpw => - //if (ctx.typer.isApplicable(tp, argType :: Nil, resultType)) - // println(i"??? $tp is applicable to $this / typeSymbol = ${tpw.typeSymbol}") - !tpw.derivesFrom(defn.FunctionClass(1)) || - ref.symbol == defn.Predef_conforms // - // as an implicit conversion, Predef.$conforms is a no-op, so exclude it - } - - def discardForValueType(tpw: Type): Boolean = tpw match { - case mt: MethodType => !mt.isImplicit - case mt: PolyType => discardForValueType(tpw.resultType) - case _ => false - } - - def discard = pt match { - case pt: ViewProto => discardForView(ref.widen, pt.argType) - case _: ValueTypeOrProto => !defn.isFunctionType(pt) && discardForValueType(ref.widen) - case _ => false - } - - (ref.symbol isAccessibleFrom ref.prefix) && { - if (discard) { - record("discarded eligible") - false - } - else NoViewsAllowed.isCompatible(normalize(ref, pt), pt) - } - } - - if (refs.isEmpty) refs - else refs filter (refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution - } - } - - /** The implicit references coming from the implicit scope of a type. - * @param tp the type determining the implicit scope - * @param companionRefs the companion objects in the implicit scope. - */ - class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) { - assert(initctx.typer != null) - lazy val refs: List[TermRef] = { - val buf = new mutable.ListBuffer[TermRef] - for (companion <- companionRefs) buf ++= companion.implicitMembers - buf.toList - } - - /** The implicit references that are eligible for expected type `tp` */ - lazy val eligible: List[TermRef] = - /*>|>*/ track("eligible in tpe") /*<|<*/ { - /*>|>*/ ctx.traceIndented(i"eligible($tp), companions = ${companionRefs.toList}%, %", implicitsDetailed, show = true) /*<|<*/ { - if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length) - filterMatching(tp) - } - } - - override def toString = - i"OfTypeImplicits($tp), companions = ${companionRefs.toList}%, %; refs = $refs%, %." - } - - /** The implicit references coming from the context. - * @param refs the implicit references made visible by the current context. - * Note: The name of the reference might be different from the name of its symbol. - * In the case of a renaming import a => b, the name of the reference is the renamed - * name, b, whereas the name of the symbol is the original name, a. - * @param outerCtx the next outer context that makes visible further implicits - */ - class ContextualImplicits(val refs: List[TermRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) { - private val eligibleCache = new mutable.AnyRefMap[Type, List[TermRef]] - - /** The implicit references that are eligible for type `tp`. */ - def eligible(tp: Type): List[TermRef] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ { - if (tp.hash == NotCached) computeEligible(tp) - else eligibleCache get tp match { - case Some(eligibles) => - def elided(ci: ContextualImplicits): Int = { - val n = ci.refs.length - if (ci.outerImplicits == NoContext.implicits) n - else n + elided(ci.outerImplicits) - } - if (monitored) record(s"elided eligible refs", elided(this)) - eligibles - case None => - val savedEphemeral = ctx.typerState.ephemeral - ctx.typerState.ephemeral = false - try { - val result = computeEligible(tp) - if (ctx.typerState.ephemeral) record("ephemeral cache miss: eligible") - else eligibleCache(tp) = result - result - } - finally ctx.typerState.ephemeral |= savedEphemeral - } - } - - private def computeEligible(tp: Type): List[TermRef] = /*>|>*/ ctx.traceIndented(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ { - if (monitored) record(s"check eligible refs in ctx", refs.length) - val ownEligible = filterMatching(tp) - if (outerImplicits == NoContext.implicits) ownEligible - else ownEligible ::: { - val shadowed = (ownEligible map (_.name)).toSet - outerImplicits.eligible(tp) filterNot (ref => shadowed contains ref.name) - } - } - - override def toString = { - val own = s"(implicits: ${refs mkString ","})" - if (outerImplicits == NoContext.implicits) own else own + "\n " + outerImplicits - } - - /** This context, or a copy, ensuring root import from symbol `root` - * is not present in outer implicits. - */ - def exclude(root: Symbol): ContextualImplicits = - if (this == NoContext.implicits) this - else { - val outerExcluded = outerImplicits exclude root - if (ctx.importInfo.site.termSymbol == root) outerExcluded - else if (outerExcluded eq outerImplicits) this - else new ContextualImplicits(refs, outerExcluded)(ctx) - } - } - - /** The result of an implicit search */ - abstract class SearchResult - - /** A successful search - * @param ref The implicit reference that succeeded - * @param tree The typed tree that needs to be inserted - * @param ctx The context after the implicit search - */ - case class SearchSuccess(tree: tpd.Tree, ref: TermRef, tstate: TyperState) extends SearchResult { - override def toString = s"SearchSuccess($tree, $ref)" - } - - /** A failed search */ - abstract class SearchFailure extends SearchResult { - /** A note describing the failure in more detail - this - * is either empty or starts with a '\n' - */ - def postscript(implicit ctx: Context): String = "" - } - - /** A "no matching implicit found" failure */ - case object NoImplicitMatches extends SearchFailure - - /** A search failure that can show information about the cause */ - abstract class ExplainedSearchFailure extends SearchFailure { - protected def pt: Type - protected def argument: tpd.Tree - protected def qualify(implicit ctx: Context) = - if (argument.isEmpty) em"match type $pt" - else em"convert from ${argument.tpe} to $pt" - - /** An explanation of the cause of the failure as a string */ - def explanation(implicit ctx: Context): String - } - - /** An ambiguous implicits failure */ - class AmbiguousImplicits(alt1: TermRef, alt2: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { - def explanation(implicit ctx: Context): String = - em"both ${err.refStr(alt1)} and ${err.refStr(alt2)} $qualify" - override def postscript(implicit ctx: Context) = - "\nNote that implicit conversions cannot be applied because they are ambiguous;" + - "\n " + explanation - } - - class NonMatchingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { - def explanation(implicit ctx: Context): String = - em"${err.refStr(ref)} does not $qualify" - } - - class ShadowedImplicit(ref: TermRef, shadowing: Type, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { - def explanation(implicit ctx: Context): String = - em"${err.refStr(ref)} does $qualify but is shadowed by ${err.refStr(shadowing)}" - } - - class DivergingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { - def explanation(implicit ctx: Context): String = - em"${err.refStr(ref)} produces a diverging implicit search when trying to $qualify" - } - - class FailedImplicit(failures: List[ExplainedSearchFailure], val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { - def explanation(implicit ctx: Context): String = - if (failures.isEmpty) s" No implicit candidates were found that $qualify" - else " " + (failures map (_.explanation) mkString "\n ") - override def postscript(implicit ctx: Context): String = - i""" - |Implicit search failure summary: - |$explanation""" - } -} - -import Implicits._ - -/** Info relating to implicits that is kept for one run */ -trait ImplicitRunInfo { self: RunInfo => - - private val implicitScopeCache = mutable.AnyRefMap[Type, OfTypeImplicits]() - - /** The implicit scope of a type `tp` - * @param liftingCtx A context to be used when computing the class symbols of - * a type. Types may contain type variables with their instances - * recorded in the current context. To find out the instance of - * a type variable, we need the current context, the current - * runinfo context does not do. - */ - def implicitScope(tp: Type, liftingCtx: Context): OfTypeImplicits = { - - val seen: mutable.Set[Type] = mutable.Set() - - /** Replace every typeref that does not refer to a class by a conjunction of class types - * that has the same implicit scope as the original typeref. The motivation for applying - * this map is that it reduces the total number of types for which we need to - * compute and cache the implicit scope; all variations wrt type parameters or - * abstract types are eliminated. - */ - object liftToClasses extends TypeMap { - override implicit protected val ctx: Context = liftingCtx - override def stopAtStatic = true - def apply(tp: Type) = tp match { - case tp: TypeRef if tp.symbol.isAbstractOrAliasType => - val pre = tp.prefix - def joinClass(tp: Type, cls: ClassSymbol) = - AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner)) - val lead = if (tp.prefix eq NoPrefix) defn.AnyType else apply(tp.prefix) - (lead /: tp.classSymbols)(joinClass) - case tp: TypeVar => - apply(tp.underlying) - case tp: HKApply => - def applyArg(arg: Type) = arg match { - case TypeBounds(lo, hi) => AndType.make(lo, hi) - case _: WildcardType => defn.AnyType - case _ => arg - } - (apply(tp.tycon) /: tp.args)((tc, arg) => AndType.make(tc, applyArg(arg))) - case tp: PolyType => - apply(tp.resType) - case _ => - mapOver(tp) - } - } - - def iscopeRefs(tp: Type): TermRefSet = - if (seen contains tp) EmptyTermRefSet - else { - seen += tp - iscope(tp).companionRefs - } - - // todo: compute implicits directly, without going via companionRefs? - def collectCompanions(tp: Type): TermRefSet = track("computeImplicitScope") { - ctx.traceIndented(i"collectCompanions($tp)", implicits) { - val comps = new TermRefSet - tp match { - case tp: NamedType => - val pre = tp.prefix - comps ++= iscopeRefs(pre) - def addClassScope(cls: ClassSymbol): Unit = { - def addRef(companion: TermRef): Unit = { - val compSym = companion.symbol - if (compSym is Package) - addRef(TermRef.withSig(companion, nme.PACKAGE, Signature.NotAMethod)) - else if (compSym.exists) - comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef] - } - def addParentScope(parent: TypeRef): Unit = { - iscopeRefs(parent) foreach addRef - for (param <- parent.typeParamSymbols) - comps ++= iscopeRefs(tp.member(param.name).info) - } - val companion = cls.companionModule - if (companion.exists) addRef(companion.valRef) - cls.classParents foreach addParentScope - } - tp.classSymbols(liftingCtx) foreach addClassScope - case _ => - // We exclude lower bounds to conform to SLS 7.2: - // "The parts of a type T are: [...] if T is an abstract type, the parts of its upper bound" - for (part <- tp.namedPartsWith(_.isType, excludeLowerBounds = true)) - comps ++= iscopeRefs(part) - } - comps - } - } - - /** The implicit scope of type `tp` - * @param isLifted Type `tp` is the result of a `liftToClasses` application - */ - def iscope(tp: Type, isLifted: Boolean = false): OfTypeImplicits = { - def computeIScope(cacheResult: Boolean) = { - val savedEphemeral = ctx.typerState.ephemeral - ctx.typerState.ephemeral = false - try { - val liftedTp = if (isLifted) tp else liftToClasses(tp) - val refs = - if (liftedTp ne tp) - iscope(liftedTp, isLifted = true).companionRefs - else - collectCompanions(tp) - val result = new OfTypeImplicits(tp, refs)(ctx) - if (ctx.typerState.ephemeral) record("ephemeral cache miss: implicitScope") - else if (cacheResult) implicitScopeCache(tp) = result - result - } - finally ctx.typerState.ephemeral |= savedEphemeral - } - - if (tp.hash == NotCached || !Config.cacheImplicitScopes) - computeIScope(cacheResult = false) - else implicitScopeCache get tp match { - case Some(is) => is - case None => - // Implicit scopes are tricky to cache because of loops. For example - // in `tests/pos/implicit-scope-loop.scala`, the scope of B contains - // the scope of A which contains the scope of B. We break the loop - // by returning EmptyTermRefSet in `collectCompanions` for types - // that we have already seen, but this means that we cannot cache - // the computed scope of A, it is incomplete. - // Keeping track of exactly where these loops happen would require a - // lot of book-keeping, instead we choose to be conservative and only - // cache scopes before any type has been seen. This is unfortunate - // because loops are very common for types in scala.collection. - computeIScope(cacheResult = seen.isEmpty) - } - } - - iscope(tp) - } - - /** A map that counts the number of times an implicit ref was picked */ - val useCount = new mutable.HashMap[TermRef, Int] { - override def default(key: TermRef) = 0 - } - - def clear() = implicitScopeCache.clear() -} - -/** The implicit resolution part of type checking */ -trait Implicits { self: Typer => - - import tpd._ - - override def viewExists(from: Type, to: Type)(implicit ctx: Context): Boolean = ( - !from.isError - && !to.isError - && !ctx.isAfterTyper - && (ctx.mode is Mode.ImplicitsEnabled) - && from.isValueType - && ( from.isValueSubType(to) - || inferView(dummyTreeOfType(from), to) - (ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState) - .isInstanceOf[SearchSuccess] - ) - ) - - /** Find an implicit conversion to apply to given tree `from` so that the - * result is compatible with type `to`. - */ - def inferView(from: Tree, to: Type)(implicit ctx: Context): SearchResult = track("inferView") { - if ( (to isRef defn.AnyClass) - || (to isRef defn.ObjectClass) - || (to isRef defn.UnitClass) - || (from.tpe isRef defn.NothingClass) - || (from.tpe isRef defn.NullClass) - || (from.tpe eq NoPrefix)) NoImplicitMatches - else - try inferImplicit(to.stripTypeVar.widenExpr, from, from.pos) - catch { - case ex: AssertionError => - implicits.println(s"view $from ==> $to") - implicits.println(ctx.typerState.constraint.show) - implicits.println(TypeComparer.explained(implicit ctx => from.tpe <:< to)) - throw ex - } - } - - /** Find an implicit argument for parameter `formal`. - * @param error An error handler that gets an error message parameter - * which is itself parameterized by another string, - * indicating where the implicit parameter is needed - */ - def inferImplicitArg(formal: Type, error: (String => String) => Unit, pos: Position)(implicit ctx: Context): Tree = - inferImplicit(formal, EmptyTree, pos) match { - case SearchSuccess(arg, _, _) => - arg - case ambi: AmbiguousImplicits => - error(where => s"ambiguous implicits: ${ambi.explanation} of $where") - EmptyTree - case failure: SearchFailure => - val arg = synthesizedClassTag(formal, pos) - if (!arg.isEmpty) arg - else { - var msgFn = (where: String) => - em"no implicit argument of type $formal found for $where" + failure.postscript - for { - notFound <- formal.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot) - Trees.Literal(Constant(raw: String)) <- notFound.argument(0) - } { - msgFn = where => - err.implicitNotFoundString( - raw, - formal.typeSymbol.typeParams.map(_.name.unexpandedName.toString), - formal.argInfos) - } - error(msgFn) - EmptyTree - } - } - - /** If `formal` is of the form ClassTag[T], where `T` is a class type, - * synthesize a class tag for `T`. - */ - def synthesizedClassTag(formal: Type, pos: Position)(implicit ctx: Context): Tree = { - if (formal.isRef(defn.ClassTagClass)) - formal.argTypes match { - case arg :: Nil => - val tp = fullyDefinedType(arg, "ClassTag argument", pos) - if (hasStableErasure(tp)) - return ref(defn.ClassTagModule) - .select(nme.apply) - .appliedToType(tp) - .appliedTo(clsOf(erasure(tp))) - .withPos(pos) - case _ => - } - EmptyTree - } - - private def assumedCanEqual(ltp: Type, rtp: Type)(implicit ctx: Context) = { - val lift = new TypeMap { - def apply(t: Type) = t match { - case t: TypeRef => - t.info match { - case TypeBounds(lo, hi) if lo ne hi => hi - case _ => t - } - case _ => - if (variance > 0) mapOver(t) else t - } - } - ltp.isError || rtp.isError || ltp <:< lift(rtp) || rtp <:< lift(ltp) - } - - /** Check that equality tests between types `ltp` and `rtp` make sense */ - def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit = - if (!ctx.isAfterTyper && !assumedCanEqual(ltp, rtp)) { - val res = inferImplicitArg( - defn.EqType.appliedTo(ltp, rtp), msgFun => ctx.error(msgFun(""), pos), pos) - implicits.println(i"Eq witness found: $res: ${res.tpe}") - } - - /** Find an implicit parameter or conversion. - * @param pt The expected type of the parameter or conversion. - * @param argument If an implicit conversion is searched, the argument to which - * it should be applied, EmptyTree otherwise. - * @param pos The position where errors should be reported. - * !!! todo: catch potential cycles - */ - def inferImplicit(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context): SearchResult = track("inferImplicit") { - assert(!ctx.isAfterTyper, - if (argument.isEmpty) i"missing implicit parameter of type $pt after typer" - else i"type error: ${argument.tpe} does not conform to $pt${err.whyNoMatchStr(argument.tpe, pt)}") - val prevConstr = ctx.typerState.constraint - ctx.traceIndented(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", implicits, show = true) { - assert(!pt.isInstanceOf[ExprType]) - val isearch = - if (ctx.settings.explaintypes.value) new ExplainedImplicitSearch(pt, argument, pos) - else new ImplicitSearch(pt, argument, pos) - val result = isearch.bestImplicit - result match { - case result: SearchSuccess => - result.tstate.commit() - result - case result: AmbiguousImplicits => - val deepPt = pt.deepenProto - if (deepPt ne pt) inferImplicit(deepPt, argument, pos) - else if (ctx.scala2Mode && !ctx.mode.is(Mode.OldOverloadingResolution)) { - inferImplicit(pt, argument, pos)(ctx.addMode(Mode.OldOverloadingResolution)) match { - case altResult: SearchSuccess => - ctx.migrationWarning( - s"According to new implicit resolution rules, this will be ambiguous:\n ${result.explanation}", - pos) - altResult - case _ => - result - } - } - else result - case _ => - assert(prevConstr eq ctx.typerState.constraint) - result - } - } - } - - /** An implicit search; parameters as in `inferImplicit` */ - class ImplicitSearch(protected val pt: Type, protected val argument: Tree, pos: Position)(implicit ctx: Context) { - - private def nestedContext = ctx.fresh.setMode(ctx.mode &~ Mode.ImplicitsEnabled) - - private def implicitProto(resultType: Type, f: Type => Type) = - if (argument.isEmpty) f(resultType) else ViewProto(f(argument.tpe.widen), f(resultType)) - // Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though. - - assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe.isInstanceOf[ExprType], - em"found: $argument: ${argument.tpe}, expected: $pt") - - /** The expected type for the searched implicit */ - lazy val fullProto = implicitProto(pt, identity) - - lazy val funProto = fullProto match { - case proto: ViewProto => - FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self) - case proto => proto - } - - /** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */ - val wildProto = implicitProto(pt, wildApprox(_)) - - /** Search failures; overridden in ExplainedImplicitSearch */ - protected def nonMatchingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches - protected def divergingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches - protected def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = NoImplicitMatches - protected def failedSearch: SearchFailure = NoImplicitMatches - - /** Search a list of eligible implicit references */ - def searchImplicits(eligible: List[TermRef], contextual: Boolean): SearchResult = { - val constr = ctx.typerState.constraint - - /** Try to typecheck an implicit reference */ - def typedImplicit(ref: TermRef)(implicit ctx: Context): SearchResult = track("typedImplicit") { ctx.traceIndented(i"typed implicit $ref, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) { - assert(constr eq ctx.typerState.constraint) - var generated: Tree = tpd.ref(ref).withPos(pos) - if (!argument.isEmpty) - generated = typedUnadapted( - untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil), - pt) - val generated1 = adapt(generated, pt) - lazy val shadowing = - typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)( - nestedContext.addMode(Mode.ImplicitShadowing).setExploreTyperState) - def refMatches(shadowing: Tree): Boolean = - ref.symbol == closureBody(shadowing).symbol || { - shadowing match { - case Trees.Select(qual, nme.apply) => refMatches(qual) - case _ => false - } - } - // Does there exist an implicit value of type `Eq[tp, tp]`? - def hasEq(tp: Type): Boolean = - new ImplicitSearch(defn.EqType.appliedTo(tp, tp), EmptyTree, pos).bestImplicit match { - case result: SearchSuccess => result.ref.symbol != defn.Predef_eqAny - case result: AmbiguousImplicits => true - case _ => false - } - def validEqAnyArgs(tp1: Type, tp2: Type) = { - List(tp1, tp2).foreach(fullyDefinedType(_, "eqAny argument", pos)) - assumedCanEqual(tp1, tp2) || !hasEq(tp1) && !hasEq(tp2) || - { implicits.println(i"invalid eqAny[$tp1, $tp2]"); false } - } - if (ctx.reporter.hasErrors) - nonMatchingImplicit(ref) - else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) && - !shadowing.tpe.isError && !refMatches(shadowing)) { - implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}") - shadowedImplicit(ref, methPart(shadowing).tpe) - } - else generated1 match { - case TypeApply(fn, targs @ (arg1 :: arg2 :: Nil)) - if fn.symbol == defn.Predef_eqAny && !validEqAnyArgs(arg1.tpe, arg2.tpe) => - nonMatchingImplicit(ref) - case _ => - SearchSuccess(generated1, ref, ctx.typerState) - } - }} - - /** Given a list of implicit references, produce a list of all implicit search successes, - * where the first is supposed to be the best one. - * @param pending The list of implicit references that remain to be investigated - * @param acc An accumulator of successful matches found so far. - */ - def rankImplicits(pending: List[TermRef], acc: List[SearchSuccess]): List[SearchSuccess] = pending match { - case ref :: pending1 => - val history = ctx.searchHistory nest wildProto - val result = - if (history eq ctx.searchHistory) divergingImplicit(ref) - else typedImplicit(ref)(nestedContext.setNewTyperState.setSearchHistory(history)) - result match { - case fail: SearchFailure => - rankImplicits(pending1, acc) - case best: SearchSuccess => - if (ctx.mode.is(Mode.ImplicitExploration)) best :: Nil - else { - val newPending = pending1 filter (isAsGood(_, best.ref)(nestedContext.setExploreTyperState)) - rankImplicits(newPending, best :: acc) - } - } - case nil => acc - } - - /** If the (result types of) the expected type, and both alternatives - * are all numeric value types, return the alternative which has - * the smaller numeric subtype as result type, if it exists. - * (This alternative is then discarded). - */ - def numericValueTieBreak(alt1: SearchSuccess, alt2: SearchSuccess): SearchResult = { - def isNumeric(tp: Type) = tp.typeSymbol.isNumericValueClass - def isProperSubType(tp1: Type, tp2: Type) = - tp1.isValueSubType(tp2) && !tp2.isValueSubType(tp1) - val rpt = pt.resultType - val rt1 = alt1.ref.widen.resultType - val rt2 = alt2.ref.widen.resultType - if (isNumeric(rpt) && isNumeric(rt1) && isNumeric(rt2)) - if (isProperSubType(rt1, rt2)) alt1 - else if (isProperSubType(rt2, rt1)) alt2 - else NoImplicitMatches - else NoImplicitMatches - } - - /** Convert a (possibly empty) list of search successes into a single search result */ - def condense(hits: List[SearchSuccess]): SearchResult = hits match { - case best :: alts => - alts find (alt => isAsGood(alt.ref, best.ref)(ctx.fresh.setExploreTyperState)) match { - case Some(alt) => - /* !!! DEBUG - println(i"ambiguous refs: ${hits map (_.ref) map (_.show) mkString ", "}") - isAsGood(best.ref, alt.ref, explain = true)(ctx.fresh.withExploreTyperState) - */ - numericValueTieBreak(best, alt) match { - case eliminated: SearchSuccess => condense(hits.filter(_ ne eliminated)) - case _ => new AmbiguousImplicits(best.ref, alt.ref, pt, argument) - } - case None => - ctx.runInfo.useCount(best.ref) += 1 - best - } - case Nil => - failedSearch - } - - /** Sort list of implicit references according to their popularity - * (# of times each was picked in current run). - */ - def sort(eligible: List[TermRef]) = eligible match { - case Nil => eligible - case e1 :: Nil => eligible - case e1 :: e2 :: Nil => - if (ctx.runInfo.useCount(e1) < ctx.runInfo.useCount(e2)) e2 :: e1 :: Nil - else eligible - case _ => eligible.sortBy(-ctx.runInfo.useCount(_)) - } - - condense(rankImplicits(sort(eligible), Nil)) - } - - /** Find a unique best implicit reference */ - def bestImplicit: SearchResult = { - searchImplicits(ctx.implicits.eligible(wildProto), contextual = true) match { - case result: SearchSuccess => result - case result: AmbiguousImplicits => result - case result: SearchFailure => - searchImplicits(implicitScope(wildProto).eligible, contextual = false) - } - } - - def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp, ctx) - } - - final class ExplainedImplicitSearch(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context) - extends ImplicitSearch(pt, argument, pos) { - private var myFailures = new mutable.ListBuffer[ExplainedSearchFailure] - private def record(fail: ExplainedSearchFailure) = { - myFailures += fail - fail - } - def failures = myFailures.toList - override def nonMatchingImplicit(ref: TermRef) = - record(new NonMatchingImplicit(ref, pt, argument)) - override def divergingImplicit(ref: TermRef) = - record(new DivergingImplicit(ref, pt, argument)) - override def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = - record(new ShadowedImplicit(ref, shadowing, pt, argument)) - override def failedSearch: SearchFailure = { - //println(s"wildProto = $wildProto") - //println(s"implicit scope = ${implicitScope(wildProto).companionRefs}") - new FailedImplicit(failures, pt, argument) - } - } -} - -/** Records the history of currently open implicit searches - * @param searchDepth The number of open searches. - * @param seen A map that records for each class symbol of a type - * that's currently searched for the complexity of the - * type that is searched for (wrt `typeSize`). The map - * is populated only once `searchDepth` is greater than - * the threshold given in the `XminImplicitSearchDepth` setting. - */ -class SearchHistory(val searchDepth: Int, val seen: Map[ClassSymbol, Int]) { - - /** The number of RefinementTypes in this type, after all aliases are expanded */ - private def typeSize(tp: Type)(implicit ctx: Context): Int = { - val accu = new TypeAccumulator[Int] { - def apply(n: Int, tp: Type): Int = tp match { - case tp: RefinedType => - foldOver(n + 1, tp) - case tp: TypeRef if tp.info.isAlias => - apply(n, tp.superType) - case _ => - foldOver(n, tp) - } - } - accu.apply(0, tp) - } - - /** Check for possible divergence. If one is detected return the current search history - * (this will be used as a criterion to abandon the implicit search in rankImplicits). - * If no divergence is detected, produce a new search history nested in the current one - * which records that we are now also looking for type `proto`. - * - * As long as `searchDepth` is lower than the `XminImplicitSearchDepth` value - * in settings, a new history is always produced, so the implicit search is always - * undertaken. If `searchDepth` matches or exceeds the `XminImplicitSearchDepth` value, - * we test that the new search is for a class that is either not yet in the set of - * `seen` classes, or the complexity of the type `proto` being searched for is strictly - * lower than the complexity of the type that was previously encountered and that had - * the same class symbol as `proto`. A possible divergence is detected if that test fails. - */ - def nest(proto: Type)(implicit ctx: Context): SearchHistory = { - if (searchDepth < ctx.settings.XminImplicitSearchDepth.value) - new SearchHistory(searchDepth + 1, seen) - else { - val size = typeSize(proto) - def updateMap(csyms: List[ClassSymbol], seen: Map[ClassSymbol, Int]): SearchHistory = csyms match { - case csym :: csyms1 => - seen get csym match { - // proto complexity is >= than the last time it was seen → diverge - case Some(prevSize) if size >= prevSize => this - case _ => updateMap(csyms1, seen.updated(csym, size)) - } - case _ => - new SearchHistory(searchDepth + 1, seen) - } - if (proto.classSymbols.isEmpty) this - else updateMap(proto.classSymbols, seen) - } - } -} - -/** A set of term references where equality is =:= */ -class TermRefSet(implicit ctx: Context) extends mutable.Traversable[TermRef] { - import collection.JavaConverters._ - private val elems = (new java.util.LinkedHashMap[TermSymbol, List[Type]]).asScala - - def += (ref: TermRef): Unit = { - val pre = ref.prefix - val sym = ref.symbol.asTerm - elems get sym match { - case Some(prefixes) => - if (!(prefixes exists (_ =:= pre))) elems(sym) = pre :: prefixes - case None => - elems(sym) = pre :: Nil - } - } - - def ++= (refs: TraversableOnce[TermRef]): Unit = - refs foreach += - - override def foreach[U](f: TermRef => U): Unit = - for (sym <- elems.keysIterator) - for (pre <- elems(sym)) - f(TermRef(pre, sym)) -} - -@sharable object EmptyTermRefSet extends TermRefSet()(NoContext) diff --git a/src/dotty/tools/dotc/typer/ImportInfo.scala b/src/dotty/tools/dotc/typer/ImportInfo.scala deleted file mode 100644 index 3aa289181..000000000 --- a/src/dotty/tools/dotc/typer/ImportInfo.scala +++ /dev/null @@ -1,117 +0,0 @@ -package dotty.tools -package dotc -package typer - -import ast.{tpd, untpd} -import ast.Trees._ -import core._ -import util.SimpleMap -import Symbols._, Names._, Denotations._, Types._, Contexts._, StdNames._, Flags._ -import Decorators.StringInterpolators - -object ImportInfo { - /** The import info for a root import from given symbol `sym` */ - def rootImport(refFn: () => TermRef)(implicit ctx: Context) = { - val selectors = untpd.Ident(nme.WILDCARD) :: Nil - def expr = tpd.Ident(refFn()) - def imp = tpd.Import(expr, selectors) - new ImportInfo(imp.symbol, selectors, isRootImport = true) - } -} - -/** Info relating to an import clause - * @param sym The import symbol defined by the clause - * @param selectors The selector clauses - * @param rootImport true if this is one of the implicit imports of scala, java.lang - * or Predef in the start context, false otherwise. - */ -class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], val isRootImport: Boolean = false)(implicit ctx: Context) { - - lazy val sym = symf - - /** The (TermRef) type of the qualifier of the import clause */ - def site(implicit ctx: Context): Type = { - val ImportType(expr) = sym.info - expr.tpe - } - - /** The names that are excluded from any wildcard import */ - def excluded: Set[TermName] = { ensureInitialized(); myExcluded } - - /** A mapping from renamed to original names */ - def reverseMapping: SimpleMap[TermName, TermName] = { ensureInitialized(); myMapped } - - /** The original names imported by-name before renaming */ - def originals: Set[TermName] = { ensureInitialized(); myOriginals } - - /** Does the import clause end with wildcard? */ - def isWildcardImport = { ensureInitialized(); myWildcardImport } - - private var myExcluded: Set[TermName] = null - private var myMapped: SimpleMap[TermName, TermName] = null - private var myOriginals: Set[TermName] = null - private var myWildcardImport: Boolean = false - - /** Compute info relating to the selector list */ - private def ensureInitialized(): Unit = if (myExcluded == null) { - myExcluded = Set() - myMapped = SimpleMap.Empty - myOriginals = Set() - def recur(sels: List[untpd.Tree]): Unit = sels match { - case sel :: sels1 => - sel match { - case Thicket(Ident(name: TermName) :: Ident(nme.WILDCARD) :: Nil) => - myExcluded += name - case Thicket(Ident(from: TermName) :: Ident(to: TermName) :: Nil) => - myMapped = myMapped.updated(to, from) - myExcluded += from - myOriginals += from - case Ident(nme.WILDCARD) => - myWildcardImport = true - case Ident(name: TermName) => - myMapped = myMapped.updated(name, name) - myOriginals += name - } - recur(sels1) - case nil => - } - recur(selectors) - } - - /** The implicit references imported by this import clause */ - def importedImplicits: List[TermRef] = { - val pre = site - if (isWildcardImport) { - val refs = pre.implicitMembers - if (excluded.isEmpty) refs - else refs filterNot (ref => excluded contains ref.name.toTermName) - } else - for { - renamed <- reverseMapping.keys - denot <- pre.member(reverseMapping(renamed)).altsWith(_ is Implicit) - } yield TermRef.withSigAndDenot(pre, renamed, denot.signature, denot) - } - - /** The root import symbol hidden by this symbol, or NoSymbol if no such symbol is hidden. - * Note: this computation needs to work even for un-initialized import infos, and - * is not allowed to force initialization. - */ - lazy val hiddenRoot: Symbol = { - val sym = site.termSymbol - def hasMaskingSelector = selectors exists { - case Thicket(_ :: Ident(nme.WILDCARD) :: Nil) => true - case _ => false - } - if ((defn.RootImportTypes exists (_.symbol == sym)) && hasMaskingSelector) sym else NoSymbol - } - - override def toString = { - val siteStr = site.show - val exprStr = if (siteStr endsWith ".type") siteStr dropRight 5 else siteStr - val selectorStr = selectors match { - case Ident(name) :: Nil => name.show - case _ => "{...}" - } - i"import $exprStr.$selectorStr" - } -} diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala deleted file mode 100644 index aede4974a..000000000 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ /dev/null @@ -1,362 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast._ -import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ -import Trees._ -import Constants._ -import Scopes._ -import ProtoTypes._ -import annotation.unchecked -import util.Positions._ -import util.{Stats, SimpleMap} -import util.common._ -import Decorators._ -import Uniques._ -import config.Printers.{typr, constr} -import annotation.tailrec -import reporting._ -import collection.mutable - -object Inferencing { - - import tpd._ - - /** Is type fully defined, meaning the type does not contain wildcard types - * or uninstantiated type variables. As a side effect, this will minimize - * any uninstantiated type variables, according to the given force degree, - * but only if the overall result of `isFullyDefined` is `true`. - * Variables that are successfully minimized do not count as uninstantiated. - */ - def isFullyDefined(tp: Type, force: ForceDegree.Value)(implicit ctx: Context): Boolean = { - val nestedCtx = ctx.fresh.setNewTyperState - val result = new IsFullyDefinedAccumulator(force)(nestedCtx).process(tp) - if (result) nestedCtx.typerState.commit() - result - } - - /** The fully defined type, where all type variables are forced. - * Throws an error if type contains wildcards. - */ - def fullyDefinedType(tp: Type, what: String, pos: Position)(implicit ctx: Context) = - if (isFullyDefined(tp, ForceDegree.all)) tp - else throw new Error(i"internal error: type of $what $tp is not fully defined, pos = $pos") // !!! DEBUG - - - /** Instantiate selected type variables `tvars` in type `tp` */ - def instantiateSelected(tp: Type, tvars: List[Type])(implicit ctx: Context): Unit = - new IsFullyDefinedAccumulator(new ForceDegree.Value(tvars.contains, minimizeAll = true)).process(tp) - - /** The accumulator which forces type variables using the policy encoded in `force` - * and returns whether the type is fully defined. The direction in which - * a type variable is instantiated is determined as follows: - * 1. T is minimized if the constraint over T is only from below (i.e. - * constrained lower bound != given lower bound and - * constrained upper bound == given upper bound). - * 2. T is maximized if the constraint over T is only from above (i.e. - * constrained upper bound != given upper bound and - * constrained lower bound == given lower bound). - * If (1) and (2) do not apply: - * 3. T is maximized if it appears only contravariantly in the given type. - * 4. T is minimized in all other cases. - * - * The instantiation is done in two phases: - * 1st Phase: Try to instantiate minimizable type variables to - * their lower bound. Record whether successful. - * 2nd Phase: If first phase was successful, instantiate all remaining type variables - * to their upper bound. - */ - private class IsFullyDefinedAccumulator(force: ForceDegree.Value)(implicit ctx: Context) extends TypeAccumulator[Boolean] { - private def instantiate(tvar: TypeVar, fromBelow: Boolean): Type = { - val inst = tvar.instantiate(fromBelow) - typr.println(i"forced instantiation of ${tvar.origin} = $inst") - inst - } - private var toMaximize: Boolean = false - def apply(x: Boolean, tp: Type): Boolean = tp.dealias match { - case _: WildcardType | _: ProtoType => - false - case tvar: TypeVar - if !tvar.isInstantiated && ctx.typerState.constraint.contains(tvar) => - force.appliesTo(tvar) && { - val direction = instDirection(tvar.origin) - if (direction != 0) { - //if (direction > 0) println(s"inst $tvar dir = up") - instantiate(tvar, direction < 0) - } - else { - val minimize = - force.minimizeAll || - variance >= 0 && !( - force == ForceDegree.noBottom && - defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true))) - if (minimize) instantiate(tvar, fromBelow = true) - else toMaximize = true - } - foldOver(x, tvar) - } - case tp => - foldOver(x, tp) - } - - private class UpperInstantiator(implicit ctx: Context) extends TypeAccumulator[Unit] { - def apply(x: Unit, tp: Type): Unit = { - tp match { - case tvar: TypeVar if !tvar.isInstantiated => - instantiate(tvar, fromBelow = false) - case _ => - } - foldOver(x, tp) - } - } - - def process(tp: Type): Boolean = { - val res = apply(true, tp) - if (res && toMaximize) new UpperInstantiator().apply((), tp) - res - } - } - - /** The list of uninstantiated type variables bound by some prefix of type `T` which - * occur in at least one formal parameter type of a prefix application. - * Considered prefixes are: - * - The function `f` of an application node `f(e1, .., en)` - * - The function `f` of a type application node `f[T1, ..., Tn]` - * - The prefix `p` of a selection `p.f`. - * - The result expression `e` of a block `{s1; .. sn; e}`. - */ - def tvarsInParams(tree: Tree)(implicit ctx: Context): List[TypeVar] = { - @tailrec def boundVars(tree: Tree, acc: List[TypeVar]): List[TypeVar] = tree match { - case Apply(fn, _) => boundVars(fn, acc) - case TypeApply(fn, targs) => - val tvars = targs.tpes.collect { - case tvar: TypeVar if !tvar.isInstantiated => tvar - } - boundVars(fn, acc ::: tvars) - case Select(pre, _) => boundVars(pre, acc) - case Block(_, expr) => boundVars(expr, acc) - case _ => acc - } - @tailrec def occurring(tree: Tree, toTest: List[TypeVar], acc: List[TypeVar]): List[TypeVar] = - if (toTest.isEmpty) acc - else tree match { - case Apply(fn, _) => - fn.tpe.widen match { - case mtp: MethodType => - val (occ, nocc) = toTest.partition(tvar => mtp.paramTypes.exists(tvar.occursIn)) - occurring(fn, nocc, occ ::: acc) - case _ => - occurring(fn, toTest, acc) - } - case TypeApply(fn, targs) => occurring(fn, toTest, acc) - case Select(pre, _) => occurring(pre, toTest, acc) - case Block(_, expr) => occurring(expr, toTest, acc) - case _ => acc - } - occurring(tree, boundVars(tree, Nil), Nil) - } - - /** The instantiation direction for given poly param computed - * from the constraint: - * @return 1 (maximize) if constraint is uniformly from above, - * -1 (minimize) if constraint is uniformly from below, - * 0 if unconstrained, or constraint is from below and above. - */ - private def instDirection(param: PolyParam)(implicit ctx: Context): Int = { - val constrained = ctx.typerState.constraint.fullBounds(param) - val original = param.binder.paramBounds(param.paramNum) - val cmp = ctx.typeComparer - val approxBelow = - if (!cmp.isSubTypeWhenFrozen(constrained.lo, original.lo)) 1 else 0 - val approxAbove = - if (!cmp.isSubTypeWhenFrozen(original.hi, constrained.hi)) 1 else 0 - approxAbove - approxBelow - } - - /** Recursively widen and also follow type declarations and type aliases. */ - def widenForMatchSelector(tp: Type)(implicit ctx: Context): Type = tp.widen match { - case tp: TypeRef if !tp.symbol.isClass => - widenForMatchSelector(tp.superType) - case tp: HKApply => - widenForMatchSelector(tp.superType) - case tp: AnnotatedType => - tp.derivedAnnotatedType(widenForMatchSelector(tp.tpe), tp.annot) - case tp => tp - } - - /** Following type aliases and stripping refinements and annotations, if one arrives at a - * class type reference where the class has a companion module, a reference to - * that companion module. Otherwise NoType - */ - def companionRef(tp: Type)(implicit ctx: Context): Type = - tp.underlyingClassRef(refinementOK = true) match { - case tp: TypeRef => - val companion = tp.classSymbol.companionModule - if (companion.exists) - companion.valRef.asSeenFrom(tp.prefix, companion.symbol.owner) - else NoType - case _ => NoType - } - - /** Interpolate those undetermined type variables in the widened type of this tree - * which are introduced by type application contained in the tree. - * If such a variable appears covariantly in type `tp` or does not appear at all, - * approximate it by its lower bound. Otherwise, if it appears contravariantly - * in type `tp` approximate it by its upper bound. - * @param ownedBy if it is different from NoSymbol, all type variables owned by - * `ownedBy` qualify, independent of position. - * Without that second condition, it can be that certain variables escape - * interpolation, for instance when their tree was eta-lifted, so - * the typechecked tree is no longer the tree in which the variable - * was declared. A concrete example of this phenomenon can be - * observed when compiling core.TypeOps#asSeenFrom. - */ - def interpolateUndetVars(tree: Tree, ownedBy: Symbol)(implicit ctx: Context): Unit = { - val constraint = ctx.typerState.constraint - val qualifies = (tvar: TypeVar) => - (tree contains tvar.owningTree) || ownedBy.exists && tvar.owner == ownedBy - def interpolate() = Stats.track("interpolateUndetVars") { - val tp = tree.tpe.widen - constr.println(s"interpolate undet vars in ${tp.show}, pos = ${tree.pos}, mode = ${ctx.mode}, undets = ${constraint.uninstVars map (tvar => s"${tvar.show}@${tvar.owningTree.pos}")}") - constr.println(s"qualifying undet vars: ${constraint.uninstVars filter qualifies map (tvar => s"$tvar / ${tvar.show}")}, constraint: ${constraint.show}") - - val vs = variances(tp, qualifies) - val hasUnreportedErrors = ctx.typerState.reporter match { - case r: StoreReporter if r.hasErrors => true - case _ => false - } - // Avoid interpolating variables if typerstate has unreported errors. - // Reason: The errors might reflect unsatisfiable constraints. In that - // case interpolating without taking account the constraints risks producing - // nonsensical types that then in turn produce incomprehensible errors. - // An example is in neg/i1240.scala. Without the condition in the next code line - // we get for - // - // val y: List[List[String]] = List(List(1)) - // - // i1430.scala:5: error: type mismatch: - // found : Int(1) - // required: Nothing - // val y: List[List[String]] = List(List(1)) - // ^ - // With the condition, we get the much more sensical: - // - // i1430.scala:5: error: type mismatch: - // found : Int(1) - // required: String - // val y: List[List[String]] = List(List(1)) - if (!hasUnreportedErrors) - vs foreachBinding { (tvar, v) => - if (v != 0) { - typr.println(s"interpolate ${if (v == 1) "co" else "contra"}variant ${tvar.show} in ${tp.show}") - tvar.instantiate(fromBelow = v == 1) - } - } - for (tvar <- constraint.uninstVars) - if (!(vs contains tvar) && qualifies(tvar)) { - typr.println(s"instantiating non-occurring ${tvar.show} in ${tp.show} / $tp") - tvar.instantiate(fromBelow = true) - } - } - if (constraint.uninstVars exists qualifies) interpolate() - } - - /** Instantiate undetermined type variables to that type `tp` is - * maximized and return None. If this is not possible, because a non-variant - * typevar is not uniquely determined, return that typevar in a Some. - */ - def maximizeType(tp: Type)(implicit ctx: Context): Option[TypeVar] = Stats.track("maximizeType") { - val vs = variances(tp, alwaysTrue) - var result: Option[TypeVar] = None - vs foreachBinding { (tvar, v) => - if (v == 1) tvar.instantiate(fromBelow = false) - else if (v == -1) tvar.instantiate(fromBelow = true) - else { - val bounds = ctx.typerState.constraint.fullBounds(tvar.origin) - if (!(bounds.hi <:< bounds.lo)) result = Some(tvar) - tvar.instantiate(fromBelow = false) - } - } - result - } - - type VarianceMap = SimpleMap[TypeVar, Integer] - - /** All occurrences of type vars in this type that satisfy predicate - * `include` mapped to their variances (-1/0/1) in this type, where - * -1 means: only covariant occurrences - * +1 means: only covariant occurrences - * 0 means: mixed or non-variant occurrences - * - * Note: We intentionally use a relaxed version of variance here, - * where the variance does not change under a prefix of a named type - * (the strict version makes prefixes invariant). This turns out to be - * better for type inference. In a nutshell, if a type variable occurs - * like this: - * - * (U? >: x.type) # T - * - * we want to instantiate U to x.type right away. No need to wait further. - */ - private def variances(tp: Type, include: TypeVar => Boolean)(implicit ctx: Context): VarianceMap = Stats.track("variances") { - val constraint = ctx.typerState.constraint - - object accu extends TypeAccumulator[VarianceMap] { - def setVariance(v: Int) = variance = v - def apply(vmap: VarianceMap, t: Type): VarianceMap = t match { - case t: TypeVar - if !t.isInstantiated && (ctx.typerState.constraint contains t) && include(t) => - val v = vmap(t) - if (v == null) vmap.updated(t, variance) - else if (v == variance || v == 0) vmap - else vmap.updated(t, 0) - case _ => - foldOver(vmap, t) - } - override def applyToPrefix(vmap: VarianceMap, t: NamedType) = - apply(vmap, t.prefix) - } - - /** Include in `vmap` type variables occurring in the constraints of type variables - * already in `vmap`. Specifically: - * - if `tvar` is covariant in `vmap`, include all variables in its lower bound - * (because they influence the minimal solution of `tvar`), - * - if `tvar` is contravariant in `vmap`, include all variables in its upper bound - * at flipped variances (because they influence the maximal solution of `tvar`), - * - if `tvar` is nonvariant in `vmap`, include all variables in its upper and lower - * bounds as non-variant. - * Do this in a fixpoint iteration until `vmap` stabilizes. - */ - def propagate(vmap: VarianceMap): VarianceMap = { - var vmap1 = vmap - def traverse(tp: Type) = { vmap1 = accu(vmap1, tp) } - vmap.foreachBinding { (tvar, v) => - val param = tvar.origin - val e = constraint.entry(param) - accu.setVariance(v) - if (v >= 0) { - traverse(e.bounds.lo) - constraint.lower(param).foreach(p => traverse(constraint.typeVarOfParam(p))) - } - if (v <= 0) { - traverse(e.bounds.hi) - constraint.upper(param).foreach(p => traverse(constraint.typeVarOfParam(p))) - } - } - if (vmap1 eq vmap) vmap else propagate(vmap1) - } - - propagate(accu(SimpleMap.Empty, tp)) - } -} - -/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */ -@sharable object ForceDegree { - class Value(val appliesTo: TypeVar => Boolean, val minimizeAll: Boolean) - val none = new Value(_ => false, minimizeAll = false) - val all = new Value(_ => true, minimizeAll = false) - val noBottom = new Value(_ => true, minimizeAll = false) -} - diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala deleted file mode 100644 index 3931fcaf4..000000000 --- a/src/dotty/tools/dotc/typer/Inliner.scala +++ /dev/null @@ -1,539 +0,0 @@ -package dotty.tools -package dotc -package typer - -import dotty.tools.dotc.ast.Trees.NamedArg -import dotty.tools.dotc.ast.{Trees, untpd, tpd, TreeTypeMap} -import Trees._ -import core._ -import Flags._ -import Symbols._ -import Types._ -import Decorators._ -import Constants._ -import StdNames.nme -import Contexts.Context -import Names.{Name, TermName} -import NameOps._ -import SymDenotations.SymDenotation -import Annotations._ -import transform.ExplicitOuter -import Inferencing.fullyDefinedType -import config.Printers.inlining -import ErrorReporting.errorTree -import collection.mutable -import transform.TypeUtils._ - -object Inliner { - import tpd._ - - /** Adds accessors accessors for all non-public term members accessed - * from `tree`. Non-public type members are currently left as they are. - * This means that references to a private type will lead to typing failures - * on the code when it is inlined. Less than ideal, but hard to do better (see below). - * - * @return If there are accessors generated, a thicket consisting of the rewritten `tree` - * and all accessors, otherwise the original tree. - */ - private def makeInlineable(tree: Tree)(implicit ctx: Context) = { - - /** A tree map which inserts accessors for all non-public term members accessed - * from inlined code. Accesors are collected in the `accessors` buffer. - */ - object addAccessors extends TreeMap { - val inlineMethod = ctx.owner - val accessors = new mutable.ListBuffer[MemberDef] - - /** A definition needs an accessor if it is private, protected, or qualified private */ - def needsAccessor(sym: Symbol)(implicit ctx: Context) = - sym.is(AccessFlags) || sym.privateWithin.exists - - /** The name of the next accessor to be generated */ - def accessorName(implicit ctx: Context) = - ctx.freshNames.newName(inlineMethod.name.asTermName.inlineAccessorName.toString) - - /** A fresh accessor symbol. - * - * @param tree The tree representing the original access to the non-public member - * @param accessorInfo The type of the accessor - */ - def accessorSymbol(tree: Tree, accessorInfo: Type)(implicit ctx: Context): Symbol = - ctx.newSymbol( - owner = inlineMethod.owner, - name = if (tree.isTerm) accessorName.toTermName else accessorName.toTypeName, - flags = if (tree.isTerm) Synthetic | Method else Synthetic, - info = accessorInfo, - coord = tree.pos).entered - - /** Add an accessor to a non-public method and replace the original access with a - * call to the accessor. - * - * @param tree The original access to the non-public symbol - * @param refPart The part that refers to the method or field of the original access - * @param targs All type arguments passed in the access, if any - * @param argss All value arguments passed in the access, if any - * @param accessedType The type of the accessed method or field, as seen from the access site. - * @param rhs A function that builds the right-hand side of the accessor, - * given a reference to the accessed symbol and any type and - * value arguments the need to be integrated. - * @return The call to the accessor method that replaces the original access. - */ - def addAccessor(tree: Tree, refPart: Tree, targs: List[Tree], argss: List[List[Tree]], - accessedType: Type, rhs: (Tree, List[Type], List[List[Tree]]) => Tree)(implicit ctx: Context): Tree = { - val qual = qualifier(refPart) - def refIsLocal = qual match { - case qual: This => qual.symbol == refPart.symbol.owner - case _ => false - } - val (accessorDef, accessorRef) = - if (refPart.symbol.isStatic || refIsLocal) { - // Easy case: Reference to a static symbol or a symbol referenced via `this.` - val accessorType = accessedType.ensureMethodic - val accessor = accessorSymbol(tree, accessorType).asTerm - val accessorDef = polyDefDef(accessor, tps => argss => - rhs(refPart, tps, argss)) - val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss) - (accessorDef, accessorRef) - } else { - // Hard case: Reference needs to go via a dynamic prefix - inlining.println(i"adding inline accessor for $tree -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))") - - // Need to dealias in order to catch all possible references to abstracted over types in - // substitutions - val dealiasMap = new TypeMap { - def apply(t: Type) = mapOver(t.dealias) - } - - val qualType = dealiasMap(qual.tpe.widen) - - // Add qualifier type as leading method argument to argument `tp` - def addQualType(tp: Type): Type = tp match { - case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, addQualType(tp.resultType)) - case tp: ExprType => addQualType(tp.resultType) - case tp => MethodType(qualType :: Nil, tp) - } - - // The types that are local to the inlined method, and that therefore have - // to be abstracted out in the accessor, which is external to the inlined method - val localRefs = qualType.namedPartsWith(_.symbol.isContainedIn(inlineMethod)).toList - - // Abstract accessed type over local refs - def abstractQualType(mtpe: Type): Type = - if (localRefs.isEmpty) mtpe - else mtpe.LambdaAbstract(localRefs.map(_.symbol)).asInstanceOf[PolyType].flatten - - val accessorType = abstractQualType(addQualType(dealiasMap(accessedType))) - val accessor = accessorSymbol(tree, accessorType).asTerm - - val accessorDef = polyDefDef(accessor, tps => argss => - rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail)) - - val accessorRef = ref(accessor) - .appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs) - .appliedToArgss((qual :: Nil) :: argss) - (accessorDef, accessorRef) - } - accessors += accessorDef - inlining.println(i"added inline accessor: $accessorDef") - accessorRef - } - - override def transform(tree: Tree)(implicit ctx: Context): Tree = super.transform { - tree match { - case _: Apply | _: TypeApply | _: RefTree if needsAccessor(tree.symbol) => - if (tree.isTerm) { - val (methPart, targs, argss) = decomposeCall(tree) - addAccessor(tree, methPart, targs, argss, - accessedType = methPart.tpe.widen, - rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss)) - } else { - // TODO: Handle references to non-public types. - // This is quite tricky, as such types can appear anywhere, including as parts - // of types of other things. For the moment we do nothing and complain - // at the implicit expansion site if there's a reference to an inaccessible type. - // Draft code (incomplete): - // - // val accessor = accessorSymbol(tree, TypeAlias(tree.tpe)).asType - // myAccessors += TypeDef(accessor) - // ref(accessor) - // - tree - } - case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) => - addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil, - accessedType = MethodType(rhs.tpe.widen :: Nil, defn.UnitType), - rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head)) - case _ => tree - } - } - } - - val tree1 = addAccessors.transform(tree) - flatTree(tree1 :: addAccessors.accessors.toList) - } - - /** Register inline info for given inline method `sym`. - * - * @param sym The symbol denotatioon of the inline method for which info is registered - * @param treeExpr A function that computes the tree to be inlined, given a context - * This tree may still refer to non-public members. - * @param ctx The context to use for evaluating `treeExpr`. It needs - * to have the inlined method as owner. - */ - def registerInlineInfo( - sym: SymDenotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit = { - sym.unforcedAnnotation(defn.BodyAnnot) match { - case Some(ann: ConcreteBodyAnnotation) => - case Some(ann: LazyBodyAnnotation) if ann.isEvaluated => - case _ => - if (!ctx.isAfterTyper) { - val inlineCtx = ctx - sym.updateAnnotation(LazyBodyAnnotation { _ => - implicit val ctx: Context = inlineCtx - ctx.withNoError(treeExpr(ctx))(makeInlineable) - }) - } - } - } - - /** `sym` has an inline method with a known body to inline (note: definitions coming - * from Scala2x class files might be `@inline`, but still lack that body. - */ - def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean = - sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot) - - private def bodyAndAccessors(sym: SymDenotation)(implicit ctx: Context): (Tree, List[MemberDef]) = - sym.unforcedAnnotation(defn.BodyAnnot).get.tree match { - case Thicket(body :: accessors) => (body, accessors.asInstanceOf[List[MemberDef]]) - case body => (body, Nil) - } - - /** The body to inline for method `sym`. - * @pre hasBodyToInline(sym) - */ - def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree = - bodyAndAccessors(sym)._1 - - /** The accessors to non-public members needed by the inlinable body of `sym`. - * These accessors are dropped as a side effect of calling this method. - * @pre hasBodyToInline(sym) - */ - def removeInlineAccessors(sym: SymDenotation)(implicit ctx: Context): List[MemberDef] = { - val (body, accessors) = bodyAndAccessors(sym) - if (accessors.nonEmpty) sym.updateAnnotation(ConcreteBodyAnnotation(body)) - accessors - } - - /** Try to inline a call to a `@inline` method. Fail with error if the maximal - * inline depth is exceeded. - * - * @param tree The call to inline - * @param pt The expected type of the call. - * @return An `Inlined` node that refers to the original call and the inlined bindings - * and body that replace it. - */ - def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = - if (enclosingInlineds.length < ctx.settings.xmaxInlines.value) - new Inliner(tree, bodyToInline(tree.symbol)).inlined(pt) - else errorTree( - tree, - i"""|Maximal number of successive inlines (${ctx.settings.xmaxInlines.value}) exceeded, - |Maybe this is caused by a recursive inline method? - |You can use -Xmax:inlines to change the limit.""" - ) - - /** Replace `Inlined` node by a block that contains its bindings and expansion */ - def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = { - val reposition = new TreeMap { - override def transform(tree: Tree)(implicit ctx: Context): Tree = { - super.transform(tree).withPos(inlined.call.pos) - } - } - tpd.seq(inlined.bindings, reposition.transform(inlined.expansion)) - } - - /** The qualifier part of a Select or Ident. - * For an Ident, this is the `This` of the current class. (TODO: use elsewhere as well?) - */ - private def qualifier(tree: Tree)(implicit ctx: Context) = tree match { - case Select(qual, _) => qual - case _ => This(ctx.owner.enclosingClass.asClass) - } -} - -/** Produces an inlined version of `call` via its `inlined` method. - * - * @param call The original call to a `@inline` method - * @param rhs The body of the inline method that replaces the call. - */ -class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { - import tpd._ - import Inliner._ - - private val (methPart, targs, argss) = decomposeCall(call) - private val meth = methPart.symbol - private val prefix = qualifier(methPart) - - // Make sure all type arguments to the call are fully determined - for (targ <- targs) fullyDefinedType(targ.tpe, "inlined type argument", targ.pos) - - /** A map from parameter names of the inline method to references of the actual arguments. - * For a type argument this is the full argument type. - * For a value argument, it is a reference to either the argument value - * (if the argument is a pure expression of singleton type), or to `val` or `def` acting - * as a proxy (if the argument is something else). - */ - private val paramBinding = new mutable.HashMap[Name, Type] - - /** A map from references to (type and value) parameters of the inline method - * to their corresponding argument or proxy references, as given by `paramBinding`. - */ - private val paramProxy = new mutable.HashMap[Type, Type] - - /** A map from the classes of (direct and outer) this references in `rhs` - * to references of their proxies. - * Note that we can't index by the ThisType itself since there are several - * possible forms to express what is logicaly the same ThisType. E.g. - * - * ThisType(TypeRef(ThisType(p), cls)) - * - * vs - * - * ThisType(TypeRef(TermRef(ThisType(<root>), p), cls)) - * - * These are different (wrt ==) types but represent logically the same key - */ - private val thisProxy = new mutable.HashMap[ClassSymbol, TermRef] - - /** A buffer for bindings that define proxies for actual arguments */ - val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] - - computeParamBindings(meth.info, targs, argss) - - private def newSym(name: Name, flags: FlagSet, info: Type): Symbol = - ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos) - - /** Populate `paramBinding` and `bindingsBuf` by matching parameters with - * corresponding arguments. `bindingbuf` will be further extended later by - * proxies to this-references. - */ - private def computeParamBindings(tp: Type, targs: List[Tree], argss: List[List[Tree]]): Unit = tp match { - case tp: PolyType => - (tp.paramNames, targs).zipped.foreach { (name, arg) => - paramBinding(name) = arg.tpe.stripTypeVar - } - computeParamBindings(tp.resultType, Nil, argss) - case tp: MethodType => - (tp.paramNames, tp.paramTypes, argss.head).zipped.foreach { (name, paramtp, arg) => - def isByName = paramtp.dealias.isInstanceOf[ExprType] - paramBinding(name) = arg.tpe.stripAnnots.stripTypeVar match { - case argtpe: SingletonType if isByName || isIdempotentExpr(arg) => argtpe - case argtpe => - val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags - val (bindingFlags, bindingType) = - if (isByName) (inlineFlag | Method, ExprType(argtpe.widen)) - else (inlineFlag, argtpe.widen) - val boundSym = newSym(name, bindingFlags, bindingType).asTerm - val binding = - if (isByName) DefDef(boundSym, arg.changeOwner(ctx.owner, boundSym)) - else ValDef(boundSym, arg) - bindingsBuf += binding - boundSym.termRef - } - } - computeParamBindings(tp.resultType, targs, argss.tail) - case _ => - assert(targs.isEmpty) - assert(argss.isEmpty) - } - - /** Populate `thisProxy` and `paramProxy` as follows: - * - * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference, - * 1b. If given type refers to an instance this, create a proxy symbol and bind the thistype to - * refer to the proxy. The proxy is not yet entered in `bindingsBuf` that will come later. - * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored - * in `paramNames` under the parameter's name. This roundabout way to bind parameter - * references to proxies is done because we not known a priori what the parameter - * references of a method are (we only know the method's type, but that contains PolyParams - * and MethodParams, not TypeRefs or TermRefs. - */ - private def registerType(tpe: Type): Unit = tpe match { - case tpe: ThisType - if !ctx.owner.isContainedIn(tpe.cls) && !tpe.cls.is(Package) && - !thisProxy.contains(tpe.cls) => - if (tpe.cls.isStaticOwner) - thisProxy(tpe.cls) = tpe.cls.sourceModule.termRef - else { - val proxyName = s"${tpe.cls.name}_this".toTermName - val proxyType = tpe.asSeenFrom(prefix.tpe, meth.owner) - thisProxy(tpe.cls) = newSym(proxyName, EmptyFlags, proxyType).termRef - registerType(meth.owner.thisType) // make sure we have a base from which to outer-select - } - case tpe: NamedType - if tpe.symbol.is(Param) && tpe.symbol.owner == meth && - !paramProxy.contains(tpe) => - paramProxy(tpe) = paramBinding(tpe.name) - case _ => - } - - /** Register type of leaf node */ - private def registerLeaf(tree: Tree): Unit = tree match { - case _: This | _: Ident | _: TypeTree => - tree.tpe.foreachPart(registerType, stopAtStatic = true) - case _ => - } - - /** The Inlined node representing the inlined call */ - def inlined(pt: Type) = { - // make sure prefix is executed if it is impure - if (!isIdempotentExpr(prefix)) registerType(meth.owner.thisType) - - // Register types of all leaves of inlined body so that the `paramProxy` and `thisProxy` maps are defined. - rhs.foreachSubTree(registerLeaf) - - // The class that the this-proxy `selfSym` represents - def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol - - // The name of the outer selector that computes the rhs of `selfSym` - def outerSelector(selfSym: Symbol): TermName = classOf(selfSym).name.toTermName ++ nme.OUTER_SELECT - - // The total nesting depth of the class represented by `selfSym`. - def outerLevel(selfSym: Symbol): Int = classOf(selfSym).ownersIterator.length - - // All needed this-proxies, sorted by nesting depth of the classes they represent (innermost first) - val accessedSelfSyms = thisProxy.values.toList.map(_.symbol).sortBy(-outerLevel(_)) - - // Compute val-definitions for all this-proxies and append them to `bindingsBuf` - var lastSelf: Symbol = NoSymbol - for (selfSym <- accessedSelfSyms) { - val rhs = - if (!lastSelf.exists) - prefix - else - untpd.Select(ref(lastSelf), outerSelector(selfSym)).withType(selfSym.info) - bindingsBuf += ValDef(selfSym.asTerm, rhs) - lastSelf = selfSym - } - - // The type map to apply to the inlined tree. This maps references to this-types - // and parameters to type references of their arguments or proxies. - val typeMap = new TypeMap { - def apply(t: Type) = t match { - case t: ThisType => thisProxy.getOrElse(t.cls, t) - case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) - case t: SingletonType => paramProxy.getOrElse(t, mapOver(t)) - case t => mapOver(t) - } - } - - // The tree map to apply to the inlined tree. This maps references to this-types - // and parameters to references of their arguments or their proxies. - def treeMap(tree: Tree) = { - tree match { - case _: This => - tree.tpe match { - case thistpe: ThisType => - thisProxy.get(thistpe.cls) match { - case Some(t) => ref(t).withPos(tree.pos) - case None => tree - } - case _ => tree - } - case _: Ident => - paramProxy.get(tree.tpe) match { - case Some(t: SingletonType) if tree.isTerm => singleton(t).withPos(tree.pos) - case Some(t) if tree.isType => TypeTree(t).withPos(tree.pos) - case None => tree - } - case _ => tree - }} - - // The complete translation maps referenves to this and parameters to - // corresponding arguments or proxies on the type and term level. It also changes - // the owner from the inlined method to the current owner. - val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil) - - val expansion = inliner(rhs.withPos(call.pos)) - ctx.traceIndented(i"inlining $call\n, BINDINGS =\n${bindingsBuf.toList}%\n%\nEXPANSION =\n$expansion", inlining, show = true) { - - // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details. - val expansion1 = InlineTyper.typed(expansion, pt)(inlineContext(call)) - - /** Does given definition bind a closure that will be inlined? */ - def bindsDeadClosure(defn: ValOrDefDef) = Ident(defn.symbol.termRef) match { - case InlineableClosure(_) => !InlineTyper.retainedClosures.contains(defn.symbol) - case _ => false - } - - /** All bindings in `bindingsBuf` except bindings of inlineable closures */ - val bindings = bindingsBuf.toList.filterNot(bindsDeadClosure).map(_.withPos(call.pos)) - - tpd.Inlined(call, bindings, expansion1) - } - } - - /** An extractor for references to closure arguments that refer to `@inline` methods */ - private object InlineableClosure { - lazy val paramProxies = paramProxy.values.toSet - def unapply(tree: Ident)(implicit ctx: Context): Option[Tree] = - if (paramProxies.contains(tree.tpe)) { - bindingsBuf.find(_.name == tree.name) match { - case Some(ddef: ValDef) if ddef.symbol.is(Inline) => - ddef.rhs match { - case closure(_, meth, _) => Some(meth) - case _ => None - } - case _ => None - } - } else None - } - - /** A typer for inlined code. Its purpose is: - * 1. Implement constant folding over inlined code - * 2. Selectively expand ifs with constant conditions - * 3. Inline arguments that are inlineable closures - * 4. Make sure inlined code is type-correct. - * 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed) - */ - private object InlineTyper extends ReTyper { - - var retainedClosures = Set[Symbol]() - - override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context) = { - val tree1 = super.typedIdent(tree, pt) - tree1 match { - case InlineableClosure(_) => retainedClosures += tree.symbol - case _ => - } - tree1 - } - - override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { - val res = super.typedSelect(tree, pt) - ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.pos) - res - } - - override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = { - val cond1 = typed(tree.cond, defn.BooleanType) - cond1.tpe.widenTermRefExpr match { - case ConstantType(Constant(condVal: Boolean)) => - val selected = typed(if (condVal) tree.thenp else tree.elsep, pt) - if (isIdempotentExpr(cond1)) selected - else Block(cond1 :: Nil, selected) - case _ => - val if1 = untpd.cpy.If(tree)(cond = untpd.TypedSplice(cond1)) - super.typedIf(if1, pt) - } - } - - override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context) = tree.asInstanceOf[tpd.Tree] match { - case Apply(Select(InlineableClosure(fn), nme.apply), args) => - inlining.println(i"reducing $tree with closure $fn") - typed(fn.appliedToArgs(args), pt) - case _ => - super.typedApply(tree, pt) - } - } -} diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala deleted file mode 100644 index 148cf1da7..000000000 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ /dev/null @@ -1,1061 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast._ -import Trees._, Constants._, StdNames._, Scopes._, Denotations._, Comments._ -import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ -import ast.desugar, ast.desugar._ -import ProtoTypes._ -import util.Positions._ -import util.{Property, SourcePosition, DotClass} -import collection.mutable -import annotation.tailrec -import ErrorReporting._ -import tpd.ListOfTreeDecorator -import config.Config -import config.Printers.{typr, completions, noPrinter} -import Annotations._ -import Inferencing._ -import transform.ValueClasses._ -import TypeApplications._ -import language.implicitConversions -import reporting.diagnostic.messages._ - -trait NamerContextOps { this: Context => - - /** Enter symbol into current class, if current class is owner of current context, - * or into current scope, if not. Should always be called instead of scope.enter - * in order to make sure that updates to class members are reflected in - * finger prints. - */ - def enter(sym: Symbol): Symbol = { - ctx.owner match { - case cls: ClassSymbol => cls.enter(sym) - case _ => this.scope.openForMutations.enter(sym) - } - sym - } - - /** The denotation with the given name in current context */ - def denotNamed(name: Name): Denotation = - if (owner.isClass) - if (outer.owner == owner) { // inner class scope; check whether we are referring to self - if (scope.size == 1) { - val elem = scope.lastEntry - if (elem.name == name) return elem.sym.denot // return self - } - assert(scope.size <= 1, scope) - owner.thisType.member(name) - } - else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext. - owner.findMember(name, owner.thisType, EmptyFlags) - else - scope.denotsNamed(name).toDenot(NoPrefix) - - /** Either the current scope, or, if the current context owner is a class, - * the declarations of the current class. - */ - def effectiveScope: Scope = - if (owner != null && owner.isClass) owner.asClass.unforcedDecls - else scope - - /** The symbol (stored in some typer's symTree) of an enclosing context definition */ - def symOfContextTree(tree: untpd.Tree) = { - def go(ctx: Context): Symbol = { - ctx.typeAssigner match { - case typer: Typer => - tree.getAttachment(typer.SymOfTree) match { - case Some(sym) => sym - case None => - var cx = ctx.outer - while (cx.typeAssigner eq typer) cx = cx.outer - go(cx) - } - case _ => NoSymbol - } - } - go(this) - } - - /** Context where `sym` is defined, assuming we are in a nested context. */ - def defContext(sym: Symbol) = - outersIterator - .dropWhile(_.owner != sym) - .dropWhile(_.owner == sym) - .next - - /** The given type, unless `sym` is a constructor, in which case the - * type of the constructed instance is returned - */ - def effectiveResultType(sym: Symbol, typeParams: List[Symbol], given: Type) = - if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams map (_.typeRef)) - else given - - /** if isConstructor, make sure it has one non-implicit parameter list */ - def normalizeIfConstructor(paramSymss: List[List[Symbol]], isConstructor: Boolean) = - if (isConstructor && - (paramSymss.isEmpty || paramSymss.head.nonEmpty && (paramSymss.head.head is Implicit))) - Nil :: paramSymss - else - paramSymss - - /** The method type corresponding to given parameters and result type */ - def methodType(typeParams: List[Symbol], valueParamss: List[List[Symbol]], resultType: Type, isJava: Boolean = false)(implicit ctx: Context): Type = { - val monotpe = - (valueParamss :\ resultType) { (params, resultType) => - val make = - if (params.nonEmpty && (params.head is Implicit)) ImplicitMethodType - else if (isJava) JavaMethodType - else MethodType - if (isJava) - for (param <- params) - if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType - make.fromSymbols(params, resultType) - } - if (typeParams.nonEmpty) monotpe.LambdaAbstract(typeParams) - else if (valueParamss.isEmpty) ExprType(monotpe) - else monotpe - } - - /** Find moduleClass/sourceModule in effective scope */ - private def findModuleBuddy(name: Name)(implicit ctx: Context) = { - val scope = effectiveScope - val it = scope.lookupAll(name).filter(_ is Module) - assert(it.hasNext, s"no companion $name in $scope") - it.next - } - - /** Add moduleClass or sourceModule functionality to completer - * for a module or module class - */ - def adjustModuleCompleter(completer: LazyType, name: Name) = - if (name.isTermName) - completer withModuleClass (_ => findModuleBuddy(name.moduleClassName)) - else - completer withSourceModule (_ => findModuleBuddy(name.sourceModuleName)) -} - -/** This class creates symbols from definitions and imports and gives them - * lazy types. - * - * Timeline: - * - * During enter, trees are expanded as necessary, populating the expandedTree map. - * Symbols are created, and the symOfTree map is set up. - * - * Symbol completion causes some trees to be already typechecked and typedTree - * entries are created to associate the typed trees with the untyped expanded originals. - * - * During typer, original trees are first expanded using expandedTree. For each - * expanded member definition or import we extract and remove the corresponding symbol - * from the symOfTree map and complete it. We then consult the typedTree map to see - * whether a typed tree exists already. If yes, the typed tree is returned as result. - * Otherwise, we proceed with regular type checking. - * - * The scheme is designed to allow sharing of nodes, as long as each duplicate appears - * in a different method. - */ -class Namer { typer: Typer => - - import untpd._ - - val TypedAhead = new Property.Key[tpd.Tree] - val ExpandedTree = new Property.Key[Tree] - val SymOfTree = new Property.Key[Symbol] - - /** A partial map from unexpanded member and pattern defs and to their expansions. - * Populated during enterSyms, emptied during typer. - */ - //lazy val expandedTree = new mutable.AnyRefMap[DefTree, Tree] - /*{ - override def default(tree: DefTree) = tree // can't have defaults on AnyRefMaps :-( - }*/ - - /** A map from expanded MemberDef, PatDef or Import trees to their symbols. - * Populated during enterSyms, emptied at the point a typed tree - * with the same symbol is created (this can be when the symbol is completed - * or at the latest when the tree is typechecked. - */ - //lazy val symOfTree = new mutable.AnyRefMap[Tree, Symbol] - - /** A map from expanded trees to their typed versions. - * Populated when trees are typechecked during completion (using method typedAhead). - */ - // lazy val typedTree = new mutable.AnyRefMap[Tree, tpd.Tree] - - /** A map from method symbols to nested typers. - * Populated when methods are completed. Emptied when they are typechecked. - * The nested typer contains new versions of the four maps above including this - * one, so that trees that are shared between different DefDefs can be independently - * used as indices. It also contains a scope that contains nested parameters. - */ - lazy val nestedTyper = new mutable.AnyRefMap[Symbol, Typer] - - /** The scope of the typer. - * For nested typers this is a place parameters are entered during completion - * and where they survive until typechecking. A context with this typer also - * has this scope. - */ - val scope = newScope - - /** The symbol of the given expanded tree. */ - def symbolOfTree(tree: Tree)(implicit ctx: Context): Symbol = { - val xtree = expanded(tree) - xtree.getAttachment(TypedAhead) match { - case Some(ttree) => ttree.symbol - case none => xtree.attachment(SymOfTree) - } - } - - /** The enclosing class with given name; error if none exists */ - def enclosingClassNamed(name: TypeName, pos: Position)(implicit ctx: Context): Symbol = { - if (name.isEmpty) NoSymbol - else { - val cls = ctx.owner.enclosingClassNamed(name) - if (!cls.exists) ctx.error(s"no enclosing class or object is named $name", pos) - cls - } - } - - /** Record `sym` as the symbol defined by `tree` */ - def recordSym(sym: Symbol, tree: Tree)(implicit ctx: Context): Symbol = { - val refs = tree.attachmentOrElse(References, Nil) - if (refs.nonEmpty) { - tree.removeAttachment(References) - refs foreach (_.pushAttachment(OriginalSymbol, sym)) - } - tree.pushAttachment(SymOfTree, sym) - sym - } - - /** If this tree is a member def or an import, create a symbol of it - * and store in symOfTree map. - */ - def createSymbol(tree: Tree)(implicit ctx: Context): Symbol = { - - def privateWithinClass(mods: Modifiers) = - enclosingClassNamed(mods.privateWithin, mods.pos) - - def checkFlags(flags: FlagSet) = - if (flags.isEmpty) flags - else { - val (ok, adapted, kind) = tree match { - case tree: TypeDef => (flags.isTypeFlags, flags.toTypeFlags, "type") - case _ => (flags.isTermFlags, flags.toTermFlags, "value") - } - if (!ok) - ctx.error(i"modifier(s) `$flags' incompatible with $kind definition", tree.pos) - adapted - } - - /** Add moduleClass/sourceModule to completer if it is for a module val or class */ - def adjustIfModule(completer: LazyType, tree: MemberDef) = - if (tree.mods is Module) ctx.adjustModuleCompleter(completer, tree.name.encode) - else completer - - typr.println(i"creating symbol for $tree in ${ctx.mode}") - - def checkNoConflict(name: Name): Name = { - def errorName(msg: => String) = { - ctx.error(msg, tree.pos) - name.freshened - } - def preExisting = ctx.effectiveScope.lookup(name) - if (ctx.owner is PackageClass) - if (preExisting.isDefinedInCurrentRun) - errorName(s"${preExisting.showLocated} has already been compiled\nonce during this run") - else name - else - if ((!ctx.owner.isClass || name.isTypeName) && preExisting.exists) - errorName(i"$name is already defined as $preExisting") - else name - } - - val inSuperCall = if (ctx.mode is Mode.InSuperCall) InSuperCall else EmptyFlags - - tree match { - case tree: TypeDef if tree.isClassDef => - val name = checkNoConflict(tree.name.encode).asTypeName - val flags = checkFlags(tree.mods.flags &~ Implicit) - val cls = recordSym(ctx.newClassSymbol( - ctx.owner, name, flags | inSuperCall, - cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree), - privateWithinClass(tree.mods), tree.namePos, ctx.source.file), tree) - cls.completer.asInstanceOf[ClassCompleter].init() - cls - case tree: MemberDef => - val name = checkNoConflict(tree.name.encode) - val flags = checkFlags(tree.mods.flags) - val isDeferred = lacksDefinition(tree) - val deferred = if (isDeferred) Deferred else EmptyFlags - val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags - val inSuperCall1 = if (tree.mods is ParamOrAccessor) EmptyFlags else inSuperCall - // suppress inSuperCall for constructor parameters - val higherKinded = tree match { - case TypeDef(_, PolyTypeTree(_, _)) if isDeferred => HigherKinded - case _ => EmptyFlags - } - - // to complete a constructor, move one context further out -- this - // is the context enclosing the class. Note that the context in which a - // constructor is recorded and the context in which it is completed are - // different: The former must have the class as owner (because the - // constructor is owned by the class), the latter must not (because - // constructor parameters are interpreted as if they are outside the class). - // Don't do this for Java constructors because they need to see the import - // of the companion object, and it is not necessary for them because they - // have no implementation. - val cctx = if (tree.name == nme.CONSTRUCTOR && !(tree.mods is JavaDefined)) ctx.outer else ctx - - val completer = tree match { - case tree: TypeDef => new TypeDefCompleter(tree)(cctx) - case _ => new Completer(tree)(cctx) - } - - recordSym(ctx.newSymbol( - ctx.owner, name, flags | deferred | method | higherKinded | inSuperCall1, - adjustIfModule(completer, tree), - privateWithinClass(tree.mods), tree.namePos), tree) - case tree: Import => - recordSym(ctx.newSymbol( - ctx.owner, nme.IMPORT, Synthetic, new Completer(tree), NoSymbol, tree.pos), tree) - case _ => - NoSymbol - } - } - - /** If `sym` exists, enter it in effective scope. Check that - * package members are not entered twice in the same run. - */ - def enterSymbol(sym: Symbol)(implicit ctx: Context) = { - if (sym.exists) { - typr.println(s"entered: $sym in ${ctx.owner} and ${ctx.effectiveScope}") - ctx.enter(sym) - } - sym - } - - /** Create package if it does not yet exist. */ - private def createPackageSymbol(pid: RefTree)(implicit ctx: Context): Symbol = { - val pkgOwner = pid match { - case Ident(_) => if (ctx.owner eq defn.EmptyPackageClass) defn.RootClass else ctx.owner - case Select(qual: RefTree, _) => createPackageSymbol(qual).moduleClass - } - val existing = pkgOwner.info.decls.lookup(pid.name) - - if ((existing is Package) && (pkgOwner eq existing.owner)) existing - else { - /** If there's already an existing type, then the package is a dup of this type */ - val existingType = pkgOwner.info.decls.lookup(pid.name.toTypeName) - if (existingType.exists) { - ctx.error(PkgDuplicateSymbol(existingType), pid.pos) - ctx.newCompletePackageSymbol(pkgOwner, (pid.name ++ "$_error_").toTermName).entered - } - else ctx.newCompletePackageSymbol(pkgOwner, pid.name.asTermName).entered - } - } - - /** Expand tree and store in `expandedTree` */ - def expand(tree: Tree)(implicit ctx: Context): Unit = tree match { - case mdef: DefTree => - val expanded = desugar.defTree(mdef) - typr.println(i"Expansion: $mdef expands to $expanded") - if (expanded ne mdef) mdef.pushAttachment(ExpandedTree, expanded) - case _ => - } - - /** The expanded version of this tree, or tree itself if not expanded */ - def expanded(tree: Tree)(implicit ctx: Context): Tree = tree match { - case ddef: DefTree => ddef.attachmentOrElse(ExpandedTree, ddef) - case _ => tree - } - - /** A new context that summarizes an import statement */ - def importContext(sym: Symbol, selectors: List[Tree])(implicit ctx: Context) = - ctx.fresh.setImportInfo(new ImportInfo(sym, selectors)) - - /** A new context for the interior of a class */ - def inClassContext(selfInfo: DotClass /* Should be Type | Symbol*/)(implicit ctx: Context): Context = { - val localCtx: Context = ctx.fresh.setNewScope - selfInfo match { - case sym: Symbol if sym.exists && sym.name != nme.WILDCARD => - localCtx.scope.openForMutations.enter(sym) - case _ => - } - localCtx - } - - /** For all class definitions `stat` in `xstats`: If the companion class if - * not also defined in `xstats`, invalidate it by setting its info to - * NoType. - */ - def invalidateCompanions(pkg: Symbol, xstats: List[untpd.Tree])(implicit ctx: Context): Unit = { - val definedNames = xstats collect { case stat: NameTree => stat.name } - def invalidate(name: TypeName) = - if (!(definedNames contains name)) { - val member = pkg.info.decl(name).asSymDenotation - if (member.isClass && !(member is Package)) member.info = NoType - } - xstats foreach { - case stat: TypeDef if stat.isClassDef => - invalidate(stat.name.moduleClassName) - case _ => - } - } - - /** Expand tree and create top-level symbols for statement and enter them into symbol table */ - def index(stat: Tree)(implicit ctx: Context): Context = { - expand(stat) - indexExpanded(stat) - } - - /** Create top-level symbols for all statements in the expansion of this statement and - * enter them into symbol table - */ - def indexExpanded(origStat: Tree)(implicit ctx: Context): Context = { - def recur(stat: Tree): Context = stat match { - case pcl: PackageDef => - val pkg = createPackageSymbol(pcl.pid) - index(pcl.stats)(ctx.fresh.setOwner(pkg.moduleClass)) - invalidateCompanions(pkg, Trees.flatten(pcl.stats map expanded)) - setDocstring(pkg, stat) - ctx - case imp: Import => - importContext(createSymbol(imp), imp.selectors) - case mdef: DefTree => - val sym = enterSymbol(createSymbol(mdef)) - setDocstring(sym, origStat) - addEnumConstants(mdef, sym) - ctx - case stats: Thicket => - stats.toList.foreach(recur) - ctx - case _ => - ctx - } - recur(expanded(origStat)) - } - - /** Determines whether this field holds an enum constant. - * To qualify, the following conditions must be met: - * - The field's class has the ENUM flag set - * - The field's class extends java.lang.Enum - * - The field has the ENUM flag set - * - The field is static - * - The field is stable - */ - def isEnumConstant(vd: ValDef)(implicit ctx: Context) = { - // val ownerHasEnumFlag = - // Necessary to check because scalac puts Java's static members into the companion object - // while Scala's enum constants live directly in the class. - // We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal - // cyclic reference error. See the commit message for details. - // if (ctx.compilationUnit.isJava) ctx.owner.companionClass.is(Enum) else ctx.owner.is(Enum) - vd.mods.is(allOf(Enum, Stable, JavaStatic, JavaDefined)) // && ownerHasEnumFlag - } - - /** Add java enum constants */ - def addEnumConstants(mdef: DefTree, sym: Symbol)(implicit ctx: Context): Unit = mdef match { - case vdef: ValDef if (isEnumConstant(vdef)) => - val enumClass = sym.owner.linkedClass - if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed) - enumClass.addAnnotation(Annotation.makeChild(sym)) - case _ => - } - - - def setDocstring(sym: Symbol, tree: Tree)(implicit ctx: Context) = tree match { - case t: MemberDef if t.rawComment.isDefined => - ctx.docCtx.foreach(_.addDocstring(sym, t.rawComment)) - case _ => () - } - - /** Create top-level symbols for statements and enter them into symbol table */ - def index(stats: List[Tree])(implicit ctx: Context): Context = { - - val classDef = mutable.Map[TypeName, TypeDef]() - val moduleDef = mutable.Map[TypeName, TypeDef]() - - /** Merge the definitions of a synthetic companion generated by a case class - * and the real companion, if both exist. - */ - def mergeCompanionDefs() = { - for (cdef @ TypeDef(name, _) <- stats) - if (cdef.isClassDef) { - classDef(name) = cdef - cdef.attachmentOrElse(ExpandedTree, cdef) match { - case Thicket(cls :: mval :: (mcls @ TypeDef(_, _: Template)) :: crest) => - moduleDef(name) = mcls - case _ => - } - } - for (mdef @ ModuleDef(name, _) <- stats if !mdef.mods.is(Flags.Package)) { - val typName = name.toTypeName - val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree) - moduleDef(typName) = mcls - classDef get name.toTypeName match { - case Some(cdef) => - cdef.attachmentOrElse(ExpandedTree, cdef) match { - case Thicket(cls :: mval :: TypeDef(_, compimpl: Template) :: crest) => - val mcls1 = cpy.TypeDef(mcls)( - rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body)) - mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: Nil)) - moduleDef(typName) = mcls1 - cdef.putAttachment(ExpandedTree, Thicket(cls :: crest)) - case _ => - } - case none => - } - } - } - - def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = { - val claz = ctx.denotNamed(classTree.name.encode).symbol - val modl = ctx.denotNamed(moduleTree.name.encode).symbol - ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, modl).entered - ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, modl, claz).entered - } - - def createCompanionLinks(implicit ctx: Context): Unit = { - for (cdef @ TypeDef(name, _) <- classDef.values) { - moduleDef.getOrElse(name, EmptyTree) match { - case t: TypeDef => - createLinks(cdef, t) - case EmptyTree => - } - } - } - - stats foreach expand - mergeCompanionDefs() - val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx)) - createCompanionLinks(ctxWithStats) - ctxWithStats - } - - /** The completer of a symbol defined by a member def or import (except ClassSymbols) */ - class Completer(val original: Tree)(implicit ctx: Context) extends LazyType { - - protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original) - - protected def typeSig(sym: Symbol): Type = original match { - case original: ValDef => - if (sym is Module) moduleValSig(sym) - else valOrDefDefSig(original, sym, Nil, Nil, identity)(localContext(sym).setNewScope) - case original: DefDef => - val typer1 = ctx.typer.newLikeThis - nestedTyper(sym) = typer1 - typer1.defDefSig(original, sym)(localContext(sym).setTyper(typer1)) - case imp: Import => - try { - val expr1 = typedAheadExpr(imp.expr, AnySelectionProto) - ImportType(expr1) - } catch { - case ex: CyclicReference => - typr.println(s"error while completing ${imp.expr}") - throw ex - } - } - - final override def complete(denot: SymDenotation)(implicit ctx: Context) = { - if (completions != noPrinter && ctx.typerState != this.ctx.typerState) { - completions.println(completions.getClass.toString) - def levels(c: Context): Int = - if (c.typerState eq this.ctx.typerState) 0 - else if (c.typerState == null) -1 - else if (c.outer.typerState == c.typerState) levels(c.outer) - else levels(c.outer) + 1 - completions.println(s"!!!completing ${denot.symbol.showLocated} in buried typerState, gap = ${levels(ctx)}") - } - completeInCreationContext(denot) - } - - protected def addAnnotations(denot: SymDenotation): Unit = original match { - case original: untpd.MemberDef => - var hasInlineAnnot = false - for (annotTree <- untpd.modsDeco(original).mods.annotations) { - val cls = typedAheadAnnotation(annotTree) - val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree)) - denot.addAnnotation(ann) - if (cls == defn.InlineAnnot && denot.is(Method, butNot = Accessor)) - denot.setFlag(Inline) - } - case _ => - } - - private def addInlineInfo(denot: SymDenotation) = original match { - case original: untpd.DefDef if denot.isInlineMethod => - Inliner.registerInlineInfo( - denot, - implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs - )(localContext(denot.symbol)) - case _ => - } - - /** Intentionally left without `implicit ctx` parameter. We need - * to pick up the context at the point where the completer was created. - */ - def completeInCreationContext(denot: SymDenotation): Unit = { - addAnnotations(denot) - addInlineInfo(denot) - denot.info = typeSig(denot.symbol) - Checking.checkWellFormed(denot.symbol) - } - } - - class TypeDefCompleter(original: TypeDef)(ictx: Context) extends Completer(original)(ictx) with TypeParamsCompleter { - private var myTypeParams: List[TypeSymbol] = null - private var nestedCtx: Context = null - assert(!original.isClassDef) - - def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = { - if (myTypeParams == null) { - //println(i"completing type params of $sym in ${sym.owner}") - nestedCtx = localContext(sym).setNewScope - myTypeParams = { - implicit val ctx: Context = nestedCtx - val tparams = original.rhs match { - case PolyTypeTree(tparams, _) => tparams - case _ => Nil - } - completeParams(tparams) - tparams.map(symbolOfTree(_).asType) - } - } - myTypeParams - } - - override protected def typeSig(sym: Symbol): Type = - typeDefSig(original, sym, completerTypeParams(sym)(ictx))(nestedCtx) - } - - class ClassCompleter(cls: ClassSymbol, original: TypeDef)(ictx: Context) extends Completer(original)(ictx) { - withDecls(newScope) - - protected implicit val ctx: Context = localContext(cls).setMode(ictx.mode &~ Mode.InSuperCall) - - val TypeDef(name, impl @ Template(constr, parents, self, _)) = original - - val (params, rest) = impl.body span { - case td: TypeDef => td.mods is Param - case vd: ValDef => vd.mods is ParamAccessor - case _ => false - } - - def init() = index(params) - - /** The type signature of a ClassDef with given symbol */ - override def completeInCreationContext(denot: SymDenotation): Unit = { - - /* The type of a parent constructor. Types constructor arguments - * only if parent type contains uninstantiated type parameters. - */ - def parentType(parent: untpd.Tree)(implicit ctx: Context): Type = - if (parent.isType) { - typedAheadType(parent, AnyTypeConstructorProto).tpe - } else { - val (core, targs) = stripApply(parent) match { - case TypeApply(core, targs) => (core, targs) - case core => (core, Nil) - } - val Select(New(tpt), nme.CONSTRUCTOR) = core - val targs1 = targs map (typedAheadType(_)) - val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes - if (ptype.typeParams.isEmpty) ptype - else typedAheadExpr(parent).tpe - } - - /* Check parent type tree `parent` for the following well-formedness conditions: - * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) - * (2) If may not derive from itself - * (3) Overriding type parameters must be correctly forwarded. (@see checkTypeParamOverride) - * (4) The class is not final - * (5) If the class is sealed, it is defined in the same compilation unit as the current class - */ - def checkedParentType(parent: untpd.Tree, paramAccessors: List[Symbol]): Type = { - val ptype = parentType(parent)(ctx.superCallContext) - if (cls.isRefinementClass) ptype - else { - val pt = checkClassType(ptype, parent.pos, - traitReq = parent ne parents.head, stablePrefixReq = true) - if (pt.derivesFrom(cls)) { - val addendum = parent match { - case Select(qual: Super, _) if ctx.scala2Mode => - "\n(Note that inheriting a class of the same name is no longer allowed)" - case _ => "" - } - ctx.error(i"cyclic inheritance: $cls extends itself$addendum", parent.pos) - defn.ObjectType - } - else if (!paramAccessors.forall(checkTypeParamOverride(pt, _))) - defn.ObjectType - else { - val pclazz = pt.typeSymbol - if (pclazz.is(Final)) - ctx.error(em"cannot extend final $pclazz", cls.pos) - if (pclazz.is(Sealed) && pclazz.associatedFile != cls.associatedFile) - ctx.error(em"cannot extend sealed $pclazz in different compilation unit", cls.pos) - pt - } - } - } - - /* Check that every parameter with the same name as a visible named parameter in the parent - * class satisfies the following two conditions: - * (1) The overriding parameter is also named (i.e. not local/name mangled). - * (2) The overriding parameter is passed on directly to the parent parameter, or the - * parent parameter is not fully defined. - * @return true if conditions are satisfied, false otherwise. - */ - def checkTypeParamOverride(parent: Type, paramAccessor: Symbol): Boolean = { - var ok = true - val pname = paramAccessor.name - - def illegal(how: String): Unit = { - ctx.error(em"Illegal override of public type parameter $pname in $parent$how", paramAccessor.pos) - ok = false - } - - def checkAlias(tp: Type): Unit = tp match { - case tp: RefinedType => - if (tp.refinedName == pname) - tp.refinedInfo match { - case TypeAlias(alias) => - alias match { - case TypeRef(pre, name1) if name1 == pname && (pre =:= cls.thisType) => - // OK, parameter is passed on directly - case _ => - illegal(em".\nParameter is both redeclared and instantiated with $alias.") - } - case _ => // OK, argument is not fully defined - } - else checkAlias(tp.parent) - case _ => - } - if (parent.nonPrivateMember(paramAccessor.name).symbol.is(Param)) - if (paramAccessor is Private) - illegal("\nwith private parameter. Parameter definition needs to be prefixed with `type'.") - else - checkAlias(parent) - ok - } - - addAnnotations(denot) - - val selfInfo = - if (self.isEmpty) NoType - else if (cls.is(Module)) { - val moduleType = cls.owner.thisType select sourceModule - if (self.name == nme.WILDCARD) moduleType - else recordSym( - ctx.newSymbol(cls, self.name, self.mods.flags, moduleType, coord = self.pos), - self) - } - else createSymbol(self) - - // pre-set info, so that parent types can refer to type params - val tempInfo = new TempClassInfo(cls.owner.thisType, cls, decls, selfInfo) - denot.info = tempInfo - - // Ensure constructor is completed so that any parameter accessors - // which have type trees deriving from its parameters can be - // completed in turn. Note that parent types access such parameter - // accessors, that's why the constructor needs to be completed before - // the parent types are elaborated. - index(constr) - symbolOfTree(constr).ensureCompleted() - - index(rest)(inClassContext(selfInfo)) - - val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList - val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors))) - val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls) - typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs") - - tempInfo.finalize(denot, parentRefs) - - Checking.checkWellFormed(cls) - if (isDerivedValueClass(cls)) cls.setFlag(Final) - cls.setApplicableFlags( - (NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat))) - } - } - - /** Typecheck tree during completion, and remember result in typedtree map */ - private def typedAheadImpl(tree: Tree, pt: Type)(implicit ctx: Context): tpd.Tree = { - val xtree = expanded(tree) - xtree.getAttachment(TypedAhead) match { - case Some(ttree) => ttree - case none => - val ttree = typer.typed(tree, pt) - xtree.pushAttachment(TypedAhead, ttree) - ttree - } - } - - def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = - typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType addMode Mode.Type) - - def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = - typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType) - - def typedAheadAnnotation(tree: Tree)(implicit ctx: Context): Symbol = tree match { - case Apply(fn, _) => typedAheadAnnotation(fn) - case TypeApply(fn, _) => typedAheadAnnotation(fn) - case Select(qual, nme.CONSTRUCTOR) => typedAheadAnnotation(qual) - case New(tpt) => typedAheadType(tpt).tpe.classSymbol - } - - /** Enter and typecheck parameter list */ - def completeParams(params: List[MemberDef])(implicit ctx: Context) = { - index(params) - for (param <- params) typedAheadExpr(param) - } - - /** The signature of a module valdef. - * This will compute the corresponding module class TypeRef immediately - * without going through the defined type of the ValDef. This is necessary - * to avoid cyclic references involving imports and module val defs. - */ - def moduleValSig(sym: Symbol)(implicit ctx: Context): Type = { - val clsName = sym.name.moduleClassName - val cls = ctx.denotNamed(clsName) suchThat (_ is ModuleClass) - ctx.owner.thisType select (clsName, cls) - } - - /** The type signature of a ValDef or DefDef - * @param mdef The definition - * @param sym Its symbol - * @param paramFn A wrapping function that produces the type of the - * defined symbol, given its final return type - */ - def valOrDefDefSig(mdef: ValOrDefDef, sym: Symbol, typeParams: List[Symbol], paramss: List[List[Symbol]], paramFn: Type => Type)(implicit ctx: Context): Type = { - - def inferredType = { - /** A type for this definition that might be inherited from elsewhere: - * If this is a setter parameter, the corresponding getter type. - * If this is a class member, the conjunction of all result types - * of overridden methods. - * NoType if neither case holds. - */ - val inherited = - if (sym.owner.isTerm) NoType - else { - // TODO: Look only at member of supertype instead? - lazy val schema = paramFn(WildcardType) - val site = sym.owner.thisType - ((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) => - def instantiatedResType(info: Type, tparams: List[Symbol], paramss: List[List[Symbol]]): Type = info match { - case info: PolyType => - if (info.paramNames.length == typeParams.length) - instantiatedResType(info.instantiate(tparams.map(_.typeRef)), Nil, paramss) - else NoType - case info: MethodType => - paramss match { - case params :: paramss1 if info.paramNames.length == params.length => - instantiatedResType(info.instantiate(params.map(_.termRef)), tparams, paramss1) - case _ => - NoType - } - case _ => - if (tparams.isEmpty && paramss.isEmpty) info.widenExpr - else NoType - } - val iRawInfo = - cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema).info - val iResType = instantiatedResType(iRawInfo, typeParams, paramss).asSeenFrom(site, cls) - if (iResType.exists) - typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType") - tp & iResType - } - } - - /** The proto-type to be used when inferring the result type from - * the right hand side. This is `WildcardType` except if the definition - * is a default getter. In that case, the proto-type is the type of - * the corresponding parameter where bound parameters are replaced by - * Wildcards. - */ - def rhsProto = { - val name = sym.asTerm.name - val idx = name.defaultGetterIndex - if (idx < 0) WildcardType - else { - val original = name.defaultGetterToMethod - val meth: Denotation = - if (original.isConstructorName && (sym.owner is ModuleClass)) - sym.owner.companionClass.info.decl(nme.CONSTRUCTOR) - else - ctx.defContext(sym).denotNamed(original) - def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match { - case params :: paramss1 => - if (idx < params.length) wildApprox(params(idx)) - else paramProto(paramss1, idx - params.length) - case nil => - WildcardType - } - val defaultAlts = meth.altsWith(_.hasDefaultParams) - if (defaultAlts.length == 1) - paramProto(defaultAlts.head.info.widen.paramTypess, idx) - else - WildcardType - } - } - - // println(s"final inherited for $sym: ${inherited.toString}") !!! - // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") - def isInline = sym.is(FinalOrInline, butNot = Method | Mutable) - - // Widen rhs type and approximate `|' but keep ConstantTypes if - // definition is inline (i.e. final in Scala2). - def widenRhs(tp: Type): Type = tp.widenTermRefExpr match { - case tp: ConstantType if isInline => tp - case _ => ctx.harmonizeUnion(tp.widen) - } - - // Replace aliases to Unit by Unit itself. If we leave the alias in - // it would be erased to BoxedUnit. - def dealiasIfUnit(tp: Type) = if (tp.isRef(defn.UnitClass)) defn.UnitType else tp - - val rhsCtx = ctx.addMode(Mode.InferringReturnType) - def rhsType = typedAheadExpr(mdef.rhs, inherited orElse rhsProto)(rhsCtx).tpe - def cookedRhsType = ctx.deskolemize(dealiasIfUnit(widenRhs(rhsType))) - lazy val lhsType = fullyDefinedType(cookedRhsType, "right-hand side", mdef.pos) - //if (sym.name.toString == "y") println(i"rhs = $rhsType, cooked = $cookedRhsType") - if (inherited.exists) - if (sym.is(Final, butNot = Method) && lhsType.isInstanceOf[ConstantType]) - lhsType // keep constant types that fill in for a non-constant (to be revised when inline has landed). - else inherited - else { - if (sym is Implicit) { - val resStr = if (mdef.isInstanceOf[DefDef]) "result " else "" - ctx.error(s"${resStr}type of implicit definition needs to be given explicitly", mdef.pos) - sym.resetFlag(Implicit) - } - lhsType orElse WildcardType - } - } - - val tptProto = mdef.tpt match { - case _: untpd.DerivedTypeTree => - WildcardType - case TypeTree() => - inferredType - case TypedSplice(tpt: TypeTree) if !isFullyDefined(tpt.tpe, ForceDegree.none) => - val rhsType = typedAheadExpr(mdef.rhs, tpt.tpe).tpe - mdef match { - case mdef: DefDef if mdef.name == nme.ANON_FUN => - val hygienicType = avoid(rhsType, paramss.flatten) - if (!(hygienicType <:< tpt.tpe)) - ctx.error(i"return type ${tpt.tpe} of lambda cannot be made hygienic;\n" + - i"it is not a supertype of the hygienic type $hygienicType", mdef.pos) - //println(i"lifting $rhsType over $paramss -> $hygienicType = ${tpt.tpe}") - //println(TypeComparer.explained { implicit ctx => hygienicType <:< tpt.tpe }) - case _ => - } - WildcardType - case _ => - WildcardType - } - paramFn(typedAheadType(mdef.tpt, tptProto).tpe) - } - - /** The type signature of a DefDef with given symbol */ - def defDefSig(ddef: DefDef, sym: Symbol)(implicit ctx: Context) = { - val DefDef(name, tparams, vparamss, _, _) = ddef - val isConstructor = name == nme.CONSTRUCTOR - - // The following 3 lines replace what was previously just completeParams(tparams). - // But that can cause bad bounds being computed, as witnessed by - // tests/pos/paramcycle.scala. The problematic sequence is this: - // 0. Class constructor gets completed. - // 1. Type parameter CP of constructor gets completed - // 2. As a first step CP's bounds are set to Nothing..Any. - // 3. CP's real type bound demands the completion of corresponding type parameter DP - // of enclosing class. - // 4. Type parameter DP has a rhs a DerivedFromParam tree, as installed by - // desugar.classDef - // 5. The completion of DP then copies the current bounds of CP, which are still Nothing..Any. - // 6. The completion of CP finishes installing the real type bounds. - // Consequence: CP ends up with the wrong bounds! - // To avoid this we always complete type parameters of a class before the type parameters - // of the class constructor, but after having indexed the constructor parameters (because - // indexing is needed to provide a symbol to copy for DP's completion. - // With the patch, we get instead the following sequence: - // 0. Class constructor gets completed. - // 1. Class constructor parameter CP is indexed. - // 2. Class parameter DP starts completion. - // 3. Info of CP is computed (to be copied to DP). - // 4. CP is completed. - // 5. Info of CP is copied to DP and DP is completed. - index(tparams) - if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted()) - for (tparam <- tparams) typedAheadExpr(tparam) - - vparamss foreach completeParams - def typeParams = tparams map symbolOfTree - val paramSymss = ctx.normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor) - def wrapMethType(restpe: Type): Type = { - val restpe1 = // try to make anonymous functions non-dependent, so that they can be used in closures - if (name == nme.ANON_FUN) avoid(restpe, paramSymss.flatten) - else restpe - ctx.methodType(tparams map symbolOfTree, paramSymss, restpe1, isJava = ddef.mods is JavaDefined) - } - if (isConstructor) { - // set result type tree to unit, but take the current class as result type of the symbol - typedAheadType(ddef.tpt, defn.UnitType) - wrapMethType(ctx.effectiveResultType(sym, typeParams, NoType)) - } - else valOrDefDefSig(ddef, sym, typeParams, paramSymss, wrapMethType) - } - - def typeDefSig(tdef: TypeDef, sym: Symbol, tparamSyms: List[TypeSymbol])(implicit ctx: Context): Type = { - def abstracted(tp: Type): Type = - if (tparamSyms.nonEmpty) tp.LambdaAbstract(tparamSyms) else tp - - val dummyInfo = abstracted(TypeBounds.empty) - sym.info = dummyInfo - // Temporarily set info of defined type T to ` >: Nothing <: Any. - // This is done to avoid cyclic reference errors for F-bounds. - // This is subtle: `sym` has now an empty TypeBounds, but is not automatically - // made an abstract type. If it had been made an abstract type, it would count as an - // abstract type of its enclosing class, which might make that class an invalid - // prefix. I verified this would lead to an error when compiling io.ClassPath. - // A distilled version is in pos/prefix.scala. - // - // The scheme critically relies on an implementation detail of isRef, which - // inspects a TypeRef's info, instead of simply dealiasing alias types. - - val isDerived = tdef.rhs.isInstanceOf[untpd.DerivedTypeTree] - val rhs = tdef.rhs match { - case PolyTypeTree(_, body) => body - case rhs => rhs - } - val rhsBodyType = typedAheadType(rhs).tpe - val rhsType = if (isDerived) rhsBodyType else abstracted(rhsBodyType) - val unsafeInfo = rhsType match { - case bounds: TypeBounds => bounds - case alias => TypeAlias(alias, if (sym is Local) sym.variance else 0) - } - if (isDerived) sym.info = unsafeInfo - else { - sym.info = NoCompleter - sym.info = checkNonCyclic(sym, unsafeInfo, reportErrors = true) - } - - // Here we pay the price for the cavalier setting info to TypeBounds.empty above. - // We need to compensate by invalidating caches in references that might - // still contain the TypeBounds.empty. If we do not do this, stdlib factories - // fail with a bounds error in PostTyper. - def ensureUpToDate(tp: Type, outdated: Type) = tp match { - case tref: TypeRef if tref.info == outdated && sym.info != outdated => - tref.uncheckedSetSym(null) - case _ => - } - ensureUpToDate(sym.typeRef, dummyInfo) - ensureUpToDate(sym.typeRef.appliedTo(tparamSyms.map(_.typeRef)), TypeBounds.empty) - sym.info - } -} diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala deleted file mode 100644 index 9a20a452e..000000000 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ /dev/null @@ -1,488 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast._ -import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ -import Trees._ -import Constants._ -import Scopes._ -import annotation.unchecked -import util.Positions._ -import util.{Stats, SimpleMap} -import util.common._ -import Decorators._ -import Uniques._ -import ErrorReporting.errorType -import config.Printers.typr -import collection.mutable - -object ProtoTypes { - - import tpd._ - - /** A trait defining an `isCompatible` method. */ - trait Compatibility { - - /** Is there an implicit conversion from `tp` to `pt`? */ - def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean - - /** A type `tp` is compatible with a type `pt` if one of the following holds: - * 1. `tp` is a subtype of `pt` - * 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type - * 3. there is an implicit conversion from `tp` to `pt`. - * 4. `tp` is a numeric subtype of `pt` (this case applies even if implicit conversions are disabled) - */ - def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean = - (tp.widenExpr relaxed_<:< pt.widenExpr) || viewExists(tp, pt) - - /** Test compatibility after normalization in a fresh typerstate. */ - def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = { - val nestedCtx = ctx.fresh.setExploreTyperState - isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx) - } - - private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match { - case _: OrType => true - case pt => pt.isRef(defn.UnitClass) - } - - /** Check that the result type of the current method - * fits the given expected result type. - */ - def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match { - case pt: FunProto => - mt match { - case mt: MethodType => - mt.isDependent || constrainResult(mt.resultType, pt.resultType) - case _ => - true - } - case _: ValueTypeOrProto if !disregardProto(pt) => - mt match { - case mt: MethodType => - mt.isDependent || isCompatible(normalize(mt, pt), pt) - case _ => - isCompatible(mt, pt) - } - case _: WildcardType => - isCompatible(mt, pt) - case _ => - true - } - } - - object NoViewsAllowed extends Compatibility { - override def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean = false - } - - /** A trait for prototypes that match all types */ - trait MatchAlways extends ProtoType { - def isMatchedBy(tp1: Type)(implicit ctx: Context) = true - def map(tm: TypeMap)(implicit ctx: Context): ProtoType = this - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = x - } - - /** A class marking ignored prototypes that can be revealed by `deepenProto` */ - case class IgnoredProto(ignored: Type) extends UncachedGroundType with MatchAlways { - override def deepenProto(implicit ctx: Context): Type = ignored - } - - /** A prototype for expressions [] that are part of a selection operation: - * - * [ ].name: proto - */ - abstract case class SelectionProto(val name: Name, val memberProto: Type, val compat: Compatibility) - extends CachedProxyType with ProtoType with ValueTypeOrProto { - - override def isMatchedBy(tp1: Type)(implicit ctx: Context) = { - name == nme.WILDCARD || { - val mbr = tp1.member(name) - def qualifies(m: SingleDenotation) = - memberProto.isRef(defn.UnitClass) || - compat.normalizedCompatible(m.info, memberProto) - mbr match { // hasAltWith inlined for performance - case mbr: SingleDenotation => mbr.exists && qualifies(mbr) - case _ => mbr hasAltWith qualifies - } - } - } - - def underlying(implicit ctx: Context) = WildcardType - - def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context) = - if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this - else SelectionProto(name, memberProto, compat) - - override def equals(that: Any): Boolean = that match { - case that: SelectionProto => - (name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat) - case _ => - false - } - - def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat) - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto) - - override def deepenProto(implicit ctx: Context) = derivedSelectionProto(name, memberProto.deepenProto, compat) - - override def computeHash = addDelta(doHash(name, memberProto), if (compat eq NoViewsAllowed) 1 else 0) - } - - class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat) - - object SelectionProto { - def apply(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context): SelectionProto = { - val selproto = new CachedSelectionProto(name, memberProto, compat) - if (compat eq NoViewsAllowed) unique(selproto) else selproto - } - } - - /** Create a selection proto-type, but only one level deep; - * treat constructors specially - */ - def selectionProto(name: Name, tp: Type, typer: Typer)(implicit ctx: Context) = - if (name.isConstructorName) WildcardType - else tp match { - case tp: UnapplyFunProto => new UnapplySelectionProto(name) - case tp => SelectionProto(name, IgnoredProto(tp), typer) - } - - /** A prototype for expressions [] that are in some unspecified selection operation - * - * [].?: ? - * - * Used to indicate that expression is in a context where the only valid - * operation is further selection. In this case, the expression need not be a value. - * @see checkValue - */ - @sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed) - - /** A prototype for selections in pattern constructors */ - class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed) - - trait ApplyingProto extends ProtoType - - /** A prototype for expressions that appear in function position - * - * [](args): resultType - */ - case class FunProto(args: List[untpd.Tree], resType: Type, typer: Typer)(implicit ctx: Context) - extends UncachedGroundType with ApplyingProto { - private var myTypedArgs: List[Tree] = Nil - - override def resultType(implicit ctx: Context) = resType - - /** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */ - private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty - - /** A map recording the typer states in which arguments stored in myTypedArg were typed */ - private var evalState: SimpleMap[untpd.Tree, TyperState] = SimpleMap.Empty - - def isMatchedBy(tp: Type)(implicit ctx: Context) = - typer.isApplicable(tp, Nil, typedArgs, resultType) - - def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer) = - if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this - else new FunProto(args, resultType, typer) - - override def notApplied = WildcardType - - /** Forget the types of any arguments that have been typed producing a constraint in a - * typer state that is not yet committed into the one of the current context `ctx`. - * This is necessary to avoid "orphan" PolyParams that are referred to from - * type variables in the typed arguments, but that are not registered in the - * current constraint. A test case is pos/t1756.scala. - * @return True if all arguments have types (in particular, no types were forgotten). - */ - def allArgTypesAreCurrent()(implicit ctx: Context): Boolean = { - evalState foreachBinding { (arg, tstate) => - if (tstate.uncommittedAncestor.constraint ne ctx.typerState.constraint) { - typr.println(i"need to invalidate $arg / ${myTypedArg(arg)}, ${tstate.constraint}, current = ${ctx.typerState.constraint}") - myTypedArg = myTypedArg.remove(arg) - evalState = evalState.remove(arg) - } - } - myTypedArg.size == args.length - } - - private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree)(implicit ctx: Context): Tree = { - var targ = myTypedArg(arg) - if (targ == null) { - targ = typerFn(arg) - if (!ctx.reporter.hasPending) { - myTypedArg = myTypedArg.updated(arg, targ) - evalState = evalState.updated(arg, ctx.typerState) - } - } - targ - } - - /** The typed arguments. This takes any arguments already typed using - * `typedArg` into account. - */ - def typedArgs: List[Tree] = { - if (myTypedArgs.size != args.length) - myTypedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_))) - myTypedArgs - } - - /** Type single argument and remember the unadapted result in `myTypedArg`. - * used to avoid repeated typings of trees when backtracking. - */ - def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = { - val targ = cacheTypedArg(arg, typer.typedUnadapted(_, formal)) - typer.adapt(targ, formal, arg) - } - - private var myTupled: Type = NoType - - /** The same proto-type but with all arguments combined in a single tuple */ - def tupled: FunProto = myTupled match { - case pt: FunProto => - pt - case _ => - myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer) - tupled - } - - /** Somebody called the `tupled` method of this prototype */ - def isTupled: Boolean = myTupled.isInstanceOf[FunProto] - - override def toString = s"FunProto(${args mkString ","} => $resultType)" - - def map(tm: TypeMap)(implicit ctx: Context): FunProto = - derivedFunProto(args, tm(resultType), typer) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = - ta(ta.foldOver(x, typedArgs.tpes), resultType) - - override def deepenProto(implicit ctx: Context) = derivedFunProto(args, resultType.deepenProto, typer) - } - - - /** A prototype for expressions that appear in function position - * - * [](args): resultType, where args are known to be typed - */ - class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType, typer)(ctx) { - override def typedArgs = args - } - - /** A prototype for implicitly inferred views: - * - * []: argType => resultType - */ - abstract case class ViewProto(argType: Type, resType: Type) - extends CachedGroundType with ApplyingProto { - - override def resultType(implicit ctx: Context) = resType - - def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = - ctx.typer.isApplicable(tp, argType :: Nil, resultType) - - def derivedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) = - if ((argType eq this.argType) && (resultType eq this.resultType)) this - else ViewProto(argType, resultType) - - def map(tm: TypeMap)(implicit ctx: Context): ViewProto = derivedViewProto(tm(argType), tm(resultType)) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = - ta(ta(x, argType), resultType) - - override def deepenProto(implicit ctx: Context) = derivedViewProto(argType, resultType.deepenProto) - } - - class CachedViewProto(argType: Type, resultType: Type) extends ViewProto(argType, resultType) { - override def computeHash = doHash(argType, resultType) - } - - object ViewProto { - def apply(argType: Type, resultType: Type)(implicit ctx: Context) = - unique(new CachedViewProto(argType, resultType)) - } - - class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto( - untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer) - - /** A prototype for expressions [] that are type-parameterized: - * - * [] [targs] resultType - */ - case class PolyProto(targs: List[Type], resType: Type) extends UncachedGroundType with ProtoType { - - override def resultType(implicit ctx: Context) = resType - - override def isMatchedBy(tp: Type)(implicit ctx: Context) = { - def isInstantiatable(tp: Type) = tp.widen match { - case tp: PolyType => tp.paramNames.length == targs.length - case _ => false - } - isInstantiatable(tp) || tp.member(nme.apply).hasAltWith(d => isInstantiatable(d.info)) - } - - def derivedPolyProto(targs: List[Type], resultType: Type) = - if ((targs eq this.targs) && (resType eq this.resType)) this - else PolyProto(targs, resType) - - override def notApplied = WildcardType - - def map(tm: TypeMap)(implicit ctx: Context): PolyProto = - derivedPolyProto(targs mapConserve tm, tm(resultType)) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = - ta(ta.foldOver(x, targs), resultType) - - override def deepenProto(implicit ctx: Context) = derivedPolyProto(targs, resultType.deepenProto) - } - - /** A prototype for expressions [] that are known to be functions: - * - * [] _ - */ - @sharable object AnyFunctionProto extends UncachedGroundType with MatchAlways - - /** A prototype for type constructors that are followed by a type application */ - @sharable object AnyTypeConstructorProto extends UncachedGroundType with MatchAlways - - /** Add all parameters in given polytype `pt` to the constraint's domain. - * If the constraint contains already some of these parameters in its domain, - * make a copy of the polytype and add the copy's type parameters instead. - * Return either the original polytype, or the copy, if one was made. - * Also, if `owningTree` is non-empty, add a type variable for each parameter. - * @return The added polytype, and the list of created type variables. - */ - def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = { - val state = ctx.typerState - assert(!(ctx.typerState.isCommittable && owningTree.isEmpty), - s"inconsistent: no typevars were added to committable constraint ${state.constraint}") - - def newTypeVars(pt: PolyType): List[TypeVar] = - for (n <- (0 until pt.paramNames.length).toList) - yield new TypeVar(PolyParam(pt, n), state, owningTree, ctx.owner) - - val added = - if (state.constraint contains pt) pt.newLikeThis(pt.paramNames, pt.paramBounds, pt.resultType) - else pt - val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) - ctx.typeComparer.addToConstraint(added, tvars) - (added, tvars) - } - - /** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */ - def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1 - - /** The normalized form of a type - * - unwraps polymorphic types, tracking their parameters in the current constraint - * - skips implicit parameters; if result type depends on implicit parameter, - * replace with Wildcard. - * - converts non-dependent method types to the corresponding function types - * - dereferences parameterless method types - * - dereferences nullary method types provided the corresponding function type - * is not a subtype of the expected type. - * Note: We need to take account of the possibility of inserting a () argument list in normalization. Otherwise, a type with a - * def toString(): String - * member would not count as a valid solution for ?{toString: String}. This would then lead to an implicit - * insertion, with a nice explosion of inference search because of course every implicit result has some sort - * of toString method. The problem is solved by dereferencing nullary method types if the corresponding - * function type is not compatible with the prototype. - */ - def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") { - tp.widenSingleton match { - case poly: PolyType => normalize(constrained(poly).resultType, pt) - case mt: MethodType => - if (mt.isImplicit) - if (mt.isDependent) - mt.resultType.substParams(mt, mt.paramTypes.map(Function.const(WildcardType))) - else mt.resultType - else - if (mt.isDependent) tp - else { - val rt = normalize(mt.resultType, pt) - pt match { - case pt: IgnoredProto => mt - case pt: ApplyingProto => mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt) - case _ => - val ft = defn.FunctionOf(mt.paramTypes, rt) - if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt - } - } - case et: ExprType => et.resultType - case _ => tp - } - } - - /** Approximate occurrences of parameter types and uninstantiated typevars - * by wildcard types. - */ - final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match { - case tp: NamedType => // default case, inlined for speed - if (tp.symbol.isStatic) tp - else tp.derivedSelect(wildApprox(tp.prefix, theMap)) - case tp: RefinedType => // default case, inlined for speed - tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap)) - case tp: TypeAlias => // default case, inlined for speed - tp.derivedTypeAlias(wildApprox(tp.alias, theMap)) - case tp @ PolyParam(poly, pnum) => - def unconstrainedApprox = WildcardType(wildApprox(poly.paramBounds(pnum)).bounds) - if (ctx.mode.is(Mode.TypevarsMissContext)) - unconstrainedApprox - else - ctx.typerState.constraint.entry(tp) match { - case bounds: TypeBounds => wildApprox(WildcardType(bounds)) - case NoType => unconstrainedApprox - case inst => wildApprox(inst) - } - case MethodParam(mt, pnum) => - WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum)))) - case tp: TypeVar => - wildApprox(tp.underlying) - case tp @ HKApply(tycon, args) => - wildApprox(tycon) match { - case _: WildcardType => WildcardType // this ensures we get a * type - case tycon1 => tp.derivedAppliedType(tycon1, args.mapConserve(wildApprox(_))) - } - case tp: AndType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) - def wildBounds(tp: Type) = - if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) - if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) - WildcardType(wildBounds(tp1a) & wildBounds(tp2a)) - else - tp.derivedAndType(tp1a, tp2a) - case tp: OrType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) - if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) - WildcardType(tp1a.bounds | tp2a.bounds) - else - tp.derivedOrType(tp1a, tp2a) - case tp: LazyRef => - WildcardType - case tp: SelectionProto => - tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed) - case tp: ViewProto => - tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType)) - case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed - tp - case _ => - (if (theMap != null) theMap else new WildApproxMap).mapOver(tp) - } - - @sharable object AssignProto extends UncachedGroundType with MatchAlways - - private[ProtoTypes] class WildApproxMap(implicit ctx: Context) extends TypeMap { - def apply(tp: Type) = wildApprox(tp, this) - } - - /** Dummy tree to be used as an argument of a FunProto or ViewProto type */ - object dummyTreeOfType { - def apply(tp: Type): Tree = untpd.Literal(Constant(null)) withTypeUnchecked tp - def unapply(tree: Tree): Option[Type] = tree match { - case Literal(Constant(null)) => Some(tree.typeOpt) - case _ => None - } - } -} diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala deleted file mode 100644 index 2413c0c22..000000000 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ /dev/null @@ -1,108 +0,0 @@ -package dotty.tools.dotc -package typer - -import core._ -import Contexts._ -import Types._ -import Symbols._ -import Decorators._ -import typer.ProtoTypes._ -import ast.{tpd, untpd} -import ast.Trees._ -import scala.util.control.NonFatal -import util.Positions.Position -import config.Printers.typr - -/** A version of Typer that keeps all symbols defined and referenced in a - * previously typed tree. - * - * All definition nodes keep their symbols. All leaf nodes for idents, selects, - * and TypeTrees keep their types. Indexing is a no-op. - * - * Otherwise, everything is as in Typer. - */ -class ReTyper extends Typer { - import tpd._ - - /** Checks that the given tree has been typed */ - protected def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = { - assert(tree.hasType, i"$tree ${tree.getClass} ${tree.uniqueId}") - tree.withType(tree.typeOpt) - } - - override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = - promote(tree) - - override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { - assert(tree.hasType, tree) - val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) - } - - override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = - promote(tree) - - override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = - promote(tree) - - override def typedSuper(tree: untpd.Super, pt: Type)(implicit ctx: Context): Tree = - promote(tree) - - override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = - promote(tree) - - override def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = { - assert(tree.hasType) - val body1 = typed(tree.body, pt) - untpd.cpy.Bind(tree)(tree.name, body1).withType(tree.typeOpt) - } - - override def typedUnApply(tree: untpd.UnApply, selType: Type)(implicit ctx: Context): UnApply = { - val fun1 = typedExpr(tree.fun, AnyFunctionProto) - val implicits1 = tree.implicits.map(typedExpr(_)) - val patterns1 = tree.patterns.mapconserve(pat => typed(pat, pat.tpe)) - untpd.cpy.UnApply(tree)(fun1, implicits1, patterns1).withType(tree.tpe) - } - - override def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context) = impl.symbol - - override def retrieveSym(tree: untpd.Tree)(implicit ctx: Context): Symbol = tree.symbol - override def symbolOfTree(tree: untpd.Tree)(implicit ctx: Context): Symbol = tree.symbol - - override def localTyper(sym: Symbol) = this - - override def index(trees: List[untpd.Tree])(implicit ctx: Context) = ctx - - override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree = - fallBack(tree, ctx.typerState) - - override def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = () - - override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = - parents - - override def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree - - override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = fun.tpe match { - case mt @ MethodType(_, formals) => - val args: List[Tree] = tree.args.zipWithConserve(formals)(typedExpr(_, _)).asInstanceOf[List[Tree]] - assignType(untpd.cpy.Apply(tree)(fun, args), fun, args) - case _ => - super.handleUnexpectedFunType(tree, fun) - } - - override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = - try super.typedUnadapted(tree, pt) - catch { - case NonFatal(ex) => - if (ctx.isAfterTyper) - println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}") - throw ex - } - - override def checkVariance(tree: Tree)(implicit ctx: Context) = () - override def inferView(from: Tree, to: Type)(implicit ctx: Context): Implicits.SearchResult = - Implicits.NoImplicitMatches - override def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit = () - override def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] = mdef :: Nil -} diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala deleted file mode 100644 index 46bdbf3b3..000000000 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ /dev/null @@ -1,1526 +0,0 @@ -package dotty.tools.dotc -package typer - -import transform._ -import core._ -import config._ -import Symbols._, SymDenotations._, Types._, Contexts._, Decorators._, Flags._, Names._, NameOps._ -import StdNames._, Denotations._, Scopes._, Constants.Constant, SymUtils._ -import Annotations._ -import util.Positions._ -import scala.collection.{ mutable, immutable } -import ast._ -import Trees._ -import TreeTransforms._ -import util.DotClass -import scala.util.{Try, Success, Failure} -import config.{ScalaVersion, NoScalaVersion} -import Decorators._ -import typer.ErrorReporting._ -import DenotTransformers._ -import ValueClasses.isDerivedValueClass - -object RefChecks { - import tpd._ - - private def isDefaultGetter(name: Name): Boolean = - name.isTermName && name.asTermName.defaultGetterIndex >= 0 - - private val defaultMethodFilter = new NameFilter { - def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = isDefaultGetter(name) - } - - /** Only one overloaded alternative is allowed to define default arguments */ - private def checkOverloadedRestrictions(clazz: Symbol)(implicit ctx: Context): Unit = { - // Using the default getters (such as methodName$default$1) as a cheap way of - // finding methods with default parameters. This way, we can limit the members to - // those with the DEFAULTPARAM flag, and infer the methods. Looking for the methods - // directly requires inspecting the parameter list of every one. That modification - // shaved 95% off the time spent in this method. - - for ( - defaultGetterClass <- List(clazz, clazz.companionModule.moduleClass); - if defaultGetterClass.isClass - ) { - val defaultGetterNames = defaultGetterClass.asClass.memberNames(defaultMethodFilter) - val defaultMethodNames = defaultGetterNames map (_.asTermName.defaultGetterToMethod) - - for (name <- defaultMethodNames) { - val methods = clazz.info.member(name).alternatives.map(_.symbol) - val haveDefaults = methods.filter(_.hasDefaultParams) - if (haveDefaults.length > 1) { - val owners = haveDefaults map (_.owner) - // constructors of different classes are allowed to have defaults - if (haveDefaults.exists(x => !x.isConstructor) || owners.distinct.size < haveDefaults.size) - ctx.error( - "in " + clazz + - ", multiple overloaded alternatives of " + haveDefaults.head + - " define default arguments" + ( - if (owners.forall(_ == clazz)) "." - else ".\nThe members with defaults are defined in " + owners.map(_.showLocated).mkString("", " and ", ".")), - clazz.pos) - } - } - } - - // Check for doomed attempt to overload applyDynamic - if (clazz derivesFrom defn.DynamicClass) { - for ((_, m1 :: m2 :: _) <- (clazz.info member nme.applyDynamic).alternatives groupBy (_.symbol.typeParams.length)) { - ctx.error("implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)", - m1.symbol.pos) - } - } - } - - /** Check that self type of this class conforms to self types of parents. - * and required classes. - */ - private def checkParents(cls: Symbol)(implicit ctx: Context): Unit = cls.info match { - case cinfo: ClassInfo => - def checkSelfConforms(other: TypeRef, category: String, relation: String) = { - val otherSelf = other.givenSelfType.asSeenFrom(cls.thisType, other.classSymbol) - if (otherSelf.exists && !(cinfo.selfType <:< otherSelf)) - ctx.error(ex"$category: self type ${cinfo.selfType} of $cls does not conform to self type $otherSelf of $relation ${other.classSymbol}", cls.pos) - } - for (parent <- cinfo.classParents) - checkSelfConforms(parent, "illegal inheritance", "parent") - for (reqd <- cinfo.givenSelfType.classSymbols) - checkSelfConforms(reqd.typeRef, "missing requirement", "required") - case _ => - } - - /** Check that a class and its companion object to not both define - * a class or module with same name - */ - private def checkCompanionNameClashes(cls: Symbol)(implicit ctx: Context): Unit = - if (!(cls.owner is ModuleClass)) { - val other = cls.owner.linkedClass.info.decl(cls.name) - if (other.symbol.isClass) - ctx.error(s"name clash: ${cls.owner} defines $cls" + "\n" + - s"and its companion ${cls.owner.companionModule} also defines $other", - cls.pos) - } - - // Override checking ------------------------------------------------------------ - - /** 1. Check all members of class `clazz` for overriding conditions. - * That is for overriding member M and overridden member O: - * - * 1.1. M must have the same or stronger access privileges as O. - * 1.2. O must not be final. - * 1.3. O is deferred, or M has `override` modifier. - * 1.4. If O is stable, then so is M. - * // @M: LIFTED 1.5. Neither M nor O are a parameterized type alias - * 1.6. If O is a type alias, then M is an alias of O. - * 1.7. If O is an abstract type then - * 1.7.1 either M is an abstract type, and M's bounds are sharper than O's bounds. - * or M is a type alias or class which conforms to O's bounds. - * 1.7.2 higher-order type arguments must respect bounds on higher-order type parameters -- @M - * (explicit bounds and those implied by variance annotations) -- @see checkKindBounds - * 1.8. If O and M are values, then - * 1.8.1 M's type is a subtype of O's type, or - * 1.8.2 M is of type []S, O is of type ()T and S <: T, or - * 1.8.3 M is of type ()S, O is of type []T and S <: T, or - * 1.9. If M is a macro def, O cannot be deferred unless there's a concrete method overriding O. - * 1.10. If M is not a macro def, O cannot be a macro def. - * 2. Check that only abstract classes have deferred members - * 3. Check that concrete classes do not have deferred definitions - * that are not implemented in a subclass. - * 4. Check that every member with an `override` modifier - * overrides some other member. - * TODO check that classes are not overridden - * TODO This still needs to be cleaned up; the current version is a staright port of what was there - * before, but it looks too complicated and method bodies are far too large. - */ - private def checkAllOverrides(clazz: Symbol)(implicit ctx: Context): Unit = { - val self = clazz.thisType - var hasErrors = false - - case class MixinOverrideError(member: Symbol, msg: String) - - val mixinOverrideErrors = new mutable.ListBuffer[MixinOverrideError]() - - def printMixinOverrideErrors(): Unit = { - mixinOverrideErrors.toList match { - case List() => - case List(MixinOverrideError(_, msg)) => - ctx.error(msg, clazz.pos) - case MixinOverrideError(member, msg) :: others => - val others1 = others.map(_.member).filter(_.name != member.name).distinct - def othersMsg = { - val others1 = others.map(_.member) - .filter(_.name != member.name) - .map(_.show).distinct - if (others1.isEmpty) "" - else i";\n other members with override errors are:: $others1%, %" - } - ctx.error(msg + othersMsg, clazz.pos) - } - } - - def infoString(sym: Symbol) = infoString0(sym, sym.owner != clazz) - def infoStringWithLocation(sym: Symbol) = infoString0(sym, true) - - def infoString0(sym: Symbol, showLocation: Boolean) = { - val sym1 = sym.underlyingSymbol - def info = self.memberInfo(sym1) - i"${if (showLocation) sym1.showLocated else sym1}${ - if (sym1.isAliasType) i", which equals ${info.bounds.hi}" - else if (sym1.isAbstractType) i" with bounds$info" - else if (sym1.is(Module)) "" - else if (sym1.isTerm) i" of type $info" - else "" - }" - } - - /* Check that all conditions for overriding `other` by `member` - * of class `clazz` are met. - */ - def checkOverride(member: Symbol, other: Symbol): Unit = { - def memberTp = self.memberInfo(member) - def otherTp = self.memberInfo(other) - - ctx.debuglog("Checking validity of %s overriding %s".format(member.showLocated, other.showLocated)) - - def noErrorType = !memberTp.isErroneous && !otherTp.isErroneous - - def overrideErrorMsg(msg: String): String = { - val isConcreteOverAbstract = - (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred) - val addendum = - if (isConcreteOverAbstract) - ";\n (Note that %s is abstract,\n and is therefore overridden by concrete %s)".format( - infoStringWithLocation(other), - infoStringWithLocation(member)) - else if (ctx.settings.debug.value) - err.typeMismatchMsg(memberTp, otherTp) - else "" - - "overriding %s;\n %s %s%s".format( - infoStringWithLocation(other), infoString(member), msg, addendum) - } - - def emitOverrideError(fullmsg: String) = - if (!(hasErrors && member.is(Synthetic) && member.is(Module))) { - // suppress errors relating toi synthetic companion objects if other override - // errors (e.g. relating to the companion class) have already been reported. - if (member.owner == clazz) ctx.error(fullmsg, member.pos) - else mixinOverrideErrors += new MixinOverrideError(member, fullmsg) - hasErrors = true - } - - def overrideError(msg: String) = { - if (noErrorType) - emitOverrideError(overrideErrorMsg(msg)) - } - - def autoOverride(sym: Symbol) = - sym.is(Synthetic) && ( - desugar.isDesugaredCaseClassMethodName(member.name) || // such names are added automatically, can't have an override preset. - sym.is(Module)) // synthetic companion - - def overrideAccessError() = { - ctx.log(i"member: ${member.showLocated} ${member.flags}") // DEBUG - ctx.log(i"other: ${other.showLocated} ${other.flags}") // DEBUG - val otherAccess = (other.flags & AccessFlags).toString - overrideError("has weaker access privileges; it should be " + - (if (otherAccess == "") "public" else "at least " + otherAccess)) - } - - def compatibleTypes = - if (member.isType) { // intersection of bounds to refined types must be nonempty - member.is(BaseTypeArg) || - (memberTp frozen_<:< otherTp) || { - val jointBounds = (memberTp.bounds & otherTp.bounds).bounds - jointBounds.lo frozen_<:< jointBounds.hi - } - } - else - isDefaultGetter(member.name) || // default getters are not checked for compatibility - memberTp.overrides(otherTp) - - def domain(sym: Symbol): Set[Name] = sym.info.namedTypeParams.map(_.name) - - //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG - - // return if we already checked this combination elsewhere - if (member.owner != clazz) { - def deferredCheck = member.is(Deferred) || !other.is(Deferred) - def subOther(s: Symbol) = s derivesFrom other.owner - def subMember(s: Symbol) = s derivesFrom member.owner - - if (subOther(member.owner) && deferredCheck) { - //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG - return - } - val parentSymbols = clazz.info.parents.map(_.typeSymbol) - if (parentSymbols exists (p => subOther(p) && subMember(p) && deferredCheck)) { - //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG - return - } - if (parentSymbols forall (p => subOther(p) == subMember(p))) { - //Console.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG - return - } - } - - /* Is the intersection between given two lists of overridden symbols empty? */ - def intersectionIsEmpty(syms1: Iterator[Symbol], syms2: Iterator[Symbol]) = { - val set2 = syms2.toSet - !(syms1 exists (set2 contains _)) - } - - // o: public | protected | package-protected (aka java's default access) - // ^-may be overridden by member with access privileges-v - // m: public | public/protected | public/protected/package-protected-in-same-package-as-o - - if (member.is(Private)) // (1.1) - overrideError("has weaker access privileges; it should not be private") - - // todo: align accessibility implication checking with isAccessible in Contexts - val ob = other.accessBoundary(member.owner) - val mb = member.accessBoundary(member.owner) - def isOverrideAccessOK = ( - (member.flags & AccessFlags).isEmpty // member is public - || // - or - - (!other.is(Protected) || member.is(Protected)) && // if o is protected, so is m, and - (ob.isContainedIn(mb) || other.is(JavaProtected)) // m relaxes o's access boundary, - // or o is Java defined and protected (see #3946) - ) - if (!isOverrideAccessOK) { - overrideAccessError() - } else if (other.isClass) { - overrideError("cannot be used here - class definitions cannot be overridden") - } else if (!other.is(Deferred) && member.isClass) { - overrideError("cannot be used here - classes can only override abstract types") - } else if (other.isEffectivelyFinal) { // (1.2) - overrideError(i"cannot override final member ${other.showLocated}") - } else if (!other.is(Deferred) && - !isDefaultGetter(other.name) && - !member.isAnyOverride) { - // (*) Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to - // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. - if (autoOverride(member)) - member.setFlag(Override) - else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner)) - emitOverrideError( - clazz + " inherits conflicting members:\n " - + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member) - + "\n(Note: this can be resolved by declaring an override in " + clazz + ".)") - else - overrideError("needs `override' modifier") - } else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) { - overrideError("needs `abstract override' modifiers") - } else if (member.is(Override) && other.is(Accessor) && - other.accessedFieldOrGetter.is(Mutable, butNot = Lazy)) { - // !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here. - // !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches. - if (!ctx.settings.overrideVars.value) - overrideError("cannot override a mutable variable") - } else if (member.isAnyOverride && - !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) && - !member.is(Deferred) && !other.is(Deferred) && - intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) { - overrideError("cannot override a concrete member without a third member that's overridden by both " + - "(this rule is designed to prevent ``accidental overrides'')") - } else if (other.isStable && !member.isStable) { // (1.4) - overrideError("needs to be a stable, immutable value") - } else if (member.is(ModuleVal) && !other.isRealMethod && !other.is(Deferred | Lazy)) { - overrideError("may not override a concrete non-lazy value") - } else if (member.is(Lazy, butNot = Module) && !other.isRealMethod && !other.is(Lazy)) { - overrideError("may not override a non-lazy value") - } else if (other.is(Lazy) && !other.isRealMethod && !member.is(Lazy)) { - overrideError("must be declared lazy to override a lazy value") - } else if (other.is(Deferred) && member.is(Macro) && member.extendedOverriddenSymbols.forall(_.is(Deferred))) { // (1.9) - overrideError("cannot be used here - term macros cannot override abstract methods") - } else if (other.is(Macro) && !member.is(Macro)) { // (1.10) - overrideError("cannot be used here - only term macros can override term macros") - } else if (!compatibleTypes) { - overrideError("has incompatible type" + err.whyNoMatchStr(memberTp, otherTp)) - } else if (member.isType && domain(member) != domain(other)) { - overrideError("has different named type parameters: "+ - i"[${domain(member).toList}%, %] instead of [${domain(other).toList}%, %]") - } else { - checkOverrideDeprecated() - } - } - - /* TODO enable; right now the annotation is scala-private, so cannot be seen - * here. - */ - def checkOverrideDeprecated() = { /* - if (other.hasDeprecatedOverridingAnnotation) { - val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse "" - val msg = s"overriding ${other.fullLocationString} is deprecated$suffix" - unit.deprecationWarning(member.pos, msg) - }*/ - } - - try { - val opc = new OverridingPairs.Cursor(clazz) - while (opc.hasNext) { - checkOverride(opc.overriding, opc.overridden) - opc.next() - } - } catch { - case ex: MergeError => - val addendum = ex.tp1 match { - case tp1: ClassInfo => - "\n(Note that having same-named member classes in types of a mixin composition is no longer allowed)" - case _ => "" - } - ctx.error(ex.getMessage + addendum, clazz.pos) - } - printMixinOverrideErrors() - - // Verifying a concrete class has nothing unimplemented. - if (!clazz.is(AbstractOrTrait)) { - val abstractErrors = new mutable.ListBuffer[String] - def abstractErrorMessage = - // a little formatting polish - if (abstractErrors.size <= 2) abstractErrors mkString " " - else abstractErrors.tail.mkString(abstractErrors.head + ":\n", "\n", "") - - def abstractClassError(mustBeMixin: Boolean, msg: String): Unit = { - def prelude = ( - if (clazz.isAnonymousClass || clazz.is(Module)) "object creation impossible" - else if (mustBeMixin) clazz + " needs to be a mixin" - else clazz + " needs to be abstract") + ", since" - - if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg) - else abstractErrors += msg - } - - def hasJavaErasedOverriding(sym: Symbol): Boolean = - !ctx.erasurePhase.exists || // can't do the test, assume the best - ctx.atPhase(ctx.erasurePhase.next) { implicit ctx => - clazz.info.nonPrivateMember(sym.name).hasAltWith { alt => - alt.symbol.is(JavaDefined, butNot = Deferred) && - !sym.owner.derivesFrom(alt.symbol.owner) && - alt.matches(sym) - } - } - - def ignoreDeferred(member: SingleDenotation) = - member.isType || - member.symbol.is(SuperAccessor) || // not yet synthesized - member.symbol.is(JavaDefined) && hasJavaErasedOverriding(member.symbol) - - // 2. Check that only abstract classes have deferred members - def checkNoAbstractMembers(): Unit = { - // Avoid spurious duplicates: first gather any missing members. - val missing = clazz.thisType.abstractTermMembers.filterNot(ignoreDeferred) - // Group missing members by the name of the underlying symbol, - // to consolidate getters and setters. - val grouped: Map[Name, Seq[SingleDenotation]] = missing groupBy (_.symbol.underlyingSymbol.name) - // Dotty deviation: Added type annotation for `grouped`. - // The inferred type is Map[Symbol#ThisName, Seq[SingleDenotation]] - // but then the definition of isMultiple fails with an error: - // RefChecks.scala:379: error: type mismatch: - // found : underlying.ThisName - // required: dotty.tools.dotc.core.Symbols.Symbol#ThisName - // - // val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1 - // ^ - // As far as I can see, the complaint is correct, even under the - // old reading where Symbol#ThisName means x.ThisName forSome { val x } - - val missingMethods = grouped.toList flatMap { - case (name, syms) => - val withoutSetters = syms filterNot (_.symbol.isSetter) - if (withoutSetters.nonEmpty) withoutSetters else syms - } - - def stubImplementations: List[String] = { - // Grouping missing methods by the declaring class - val regrouped = missingMethods.groupBy(_.symbol.owner).toList - def membersStrings(members: List[SingleDenotation]) = - members.sortBy(_.symbol.name.toString).map(_.showDcl + " = ???") - - if (regrouped.tail.isEmpty) - membersStrings(regrouped.head._2) - else (regrouped.sortBy("" + _._1.name) flatMap { - case (owner, members) => - ("// Members declared in " + owner.fullName) +: membersStrings(members) :+ "" - }).init - } - - // If there are numerous missing methods, we presume they are aware of it and - // give them a nicely formatted set of method signatures for implementing. - if (missingMethods.size > 1) { - abstractClassError(false, "it has " + missingMethods.size + " unimplemented members.") - val preface = - """|/** As seen from %s, the missing signatures are as follows. - | * For convenience, these are usable as stub implementations. - | */ - |""".stripMargin.format(clazz) - abstractErrors += stubImplementations.map(" " + _ + "\n").mkString(preface, "", "") - return - } - - for (member <- missing) { - val memberSym = member.symbol - def undefined(msg: String) = - abstractClassError(false, s"${member.showDcl} is not defined $msg") - val underlying = memberSym.underlyingSymbol - - // Give a specific error message for abstract vars based on why it fails: - // It could be unimplemented, have only one accessor, or be uninitialized. - if (underlying.is(Mutable)) { - val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1 - - // If both getter and setter are missing, squelch the setter error. - if (memberSym.isSetter && isMultiple) () - else undefined( - if (memberSym.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)" - else if (memberSym.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)" - else err.abstractVarMessage(memberSym)) - } else if (underlying.is(Method)) { - // If there is a concrete method whose name matches the unimplemented - // abstract method, and a cursory examination of the difference reveals - // something obvious to us, let's make it more obvious to them. - val abstractParams = underlying.info.firstParamTypes - val matchingName = clazz.info.nonPrivateMember(underlying.name).alternatives - val matchingArity = matchingName filter { m => - !m.symbol.is(Deferred) && - m.info.firstParamTypes.length == abstractParams.length - } - - matchingArity match { - // So far so good: only one candidate method - case concrete :: Nil => - val mismatches = - abstractParams.zip(concrete.info.firstParamTypes) - .filterNot { case (x, y) => x =:= y } - mismatches match { - // Only one mismatched parameter: say something useful. - case (pa, pc) :: Nil => - val abstractSym = pa.typeSymbol - val concreteSym = pc.typeSymbol - def subclassMsg(c1: Symbol, c2: Symbol) = - s": ${c1.showLocated} is a subclass of ${c2.showLocated}, but method parameter types must match exactly." - val addendum = - if (abstractSym == concreteSym) { - val paArgs = pa.argInfos - val pcArgs = pc.argInfos - val paConstr = pa.withoutArgs(paArgs) - val pcConstr = pc.withoutArgs(pcArgs) - (paConstr, pcConstr) match { - case (TypeRef(pre1, _), TypeRef(pre2, _)) => - if (pre1 =:= pre2) ": their type parameters differ" - else ": their prefixes (i.e. enclosing instances) differ" - case _ => - "" - } - } else if (abstractSym isSubClass concreteSym) - subclassMsg(abstractSym, concreteSym) - else if (concreteSym isSubClass abstractSym) - subclassMsg(concreteSym, abstractSym) - else "" - - undefined(s"\n(Note that ${pa.show} does not match ${pc.show}$addendum)") - case xs => - undefined(s"\n(The class implements a member with a different type: ${concrete.showDcl})") - } - case Nil => - undefined("") - case concretes => - undefined(s"\n(The class implements members with different types: ${concretes.map(_.showDcl)}%\n %)") - } - } else undefined("") - } - } - - // 3. Check that concrete classes do not have deferred definitions - // that are not implemented in a subclass. - // Note that this is not the same as (2); In a situation like - // - // class C { def m: Int = 0} - // class D extends C { def m: Int } - // - // (3) is violated but not (2). - def checkNoAbstractDecls(bc: Symbol): Unit = { - for (decl <- bc.info.decls) { - if (decl.is(Deferred) && !ignoreDeferred(decl)) { - val impl = decl.matchingMember(clazz.thisType) - if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) { - val impl1 = clazz.thisType.nonPrivateMember(decl.name) // DEBUG - ctx.log(i"${impl1}: ${impl1.info}") // DEBUG - ctx.log(i"${clazz.thisType.memberInfo(decl)}") // DEBUG - abstractClassError(false, "there is a deferred declaration of " + infoString(decl) + - " which is not implemented in a subclass" + err.abstractVarMessage(decl)) - } - } - } - if (bc.asClass.superClass.is(Abstract)) - checkNoAbstractDecls(bc.asClass.superClass) - } - - checkNoAbstractMembers() - if (abstractErrors.isEmpty) - checkNoAbstractDecls(clazz) - - if (abstractErrors.nonEmpty) - ctx.error(abstractErrorMessage, clazz.pos) - } else if (clazz.is(Trait) && !(clazz derivesFrom defn.AnyValClass)) { - // For non-AnyVal classes, prevent abstract methods in interfaces that override - // final members in Object; see #4431 - for (decl <- clazz.info.decls) { - // Have to use matchingSymbol, not a method involving overridden symbols, - // because the scala type system understands that an abstract method here does not - // override a concrete method in Object. The jvm, however, does not. - val overridden = decl.matchingDecl(defn.ObjectClass, defn.ObjectType) - if (overridden.is(Final)) - ctx.error("trait cannot redefine final method from class AnyRef", decl.pos) - } - } - - /* Returns whether there is a symbol declared in class `inclazz` - * (which must be different from `clazz`) whose name and type - * seen as a member of `class.thisType` matches `member`'s. - */ - def hasMatchingSym(inclazz: Symbol, member: Symbol): Boolean = { - - def isSignatureMatch(sym: Symbol) = !sym.isTerm || - clazz.thisType.memberInfo(sym).matchesLoosely(member.info) - - /* The rules for accessing members which have an access boundary are more - * restrictive in java than scala. Since java has no concept of package nesting, - * a member with "default" (package-level) access can only be accessed by members - * in the exact same package. Example: - * - * package a.b; - * public class JavaClass { void foo() { } } - * - * The member foo() can be accessed only from members of package a.b, and not - * nested packages like a.b.c. In the analogous scala class: - * - * package a.b - * class ScalaClass { private[b] def foo() = () } - * - * The member IS accessible to classes in package a.b.c. The javaAccessCheck logic - * is restricting the set of matching signatures according to the above semantics. - */ - def javaAccessCheck(sym: Symbol) = ( - !inclazz.is(JavaDefined) // not a java defined member - || !sym.privateWithin.exists // no access boundary - || sym.is(Protected) // marked protected in java, thus accessible to subclasses - || sym.privateWithin == member.enclosingPackageClass // exact package match - ) - def classDecls = inclazz.info.nonPrivateDecl(member.name) - - (inclazz != clazz) && - classDecls.hasAltWith(d => isSignatureMatch(d.symbol) && javaAccessCheck(d.symbol)) - } - - // 4. Check that every defined member with an `override` modifier overrides some other member. - for (member <- clazz.info.decls) - if (member.isAnyOverride && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) { - // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG - - val nonMatching = clazz.info.member(member.name).altsWith(alt => alt.owner != clazz && !alt.is(Final)) - def issueError(suffix: String) = - ctx.error(i"$member overrides nothing$suffix", member.pos) - nonMatching match { - case Nil => - issueError("") - case ms => - val superSigs = ms.map(_.showDcl).mkString("\n") - issueError(s".\nNote: the super classes of ${member.owner} contain the following, non final members named ${member.name}:\n${superSigs}") - } - member.resetFlag(Override) - member.resetFlag(AbsOverride) - } - } - - // Note: if a symbol has both @deprecated and @migration annotations and both - // warnings are enabled, only the first one checked here will be emitted. - // I assume that's a consequence of some code trying to avoid noise by suppressing - // warnings after the first, but I think it'd be better if we didn't have to - // arbitrarily choose one as more important than the other. - private def checkUndesiredProperties(sym: Symbol, pos: Position)(implicit ctx: Context): Unit = { - // If symbol is deprecated, and the point of reference is not enclosed - // in either a deprecated member or a scala bridge method, issue a warning. - if (sym.isDeprecated && !ctx.owner.ownersIterator.exists(_.isDeprecated)) { - ctx.deprecationWarning("%s%s is deprecated%s".format( - sym, sym.showLocated, sym.deprecationMessage map (": " + _) getOrElse "", pos)) - } - // Similar to deprecation: check if the symbol is marked with @migration - // indicating it has changed semantics between versions. - if (sym.hasAnnotation(defn.MigrationAnnot) && ctx.settings.Xmigration.value != NoScalaVersion) { - val symVersion: scala.util.Try[ScalaVersion] = sym.migrationVersion.get - val changed = symVersion match { - case scala.util.Success(v) => - ctx.settings.Xmigration.value < v - case Failure(ex) => - ctx.warning(s"${sym.showLocated} has an unparsable version number: ${ex.getMessage()}", pos) - false - } - if (changed) - ctx.warning(s"${sym.showLocated} has changed semantics in version $symVersion:\n${sym.migrationMessage.get}") - } - /* (Not enabled yet) - * See an explanation of compileTimeOnly in its scaladoc at scala.annotation.compileTimeOnly. - * - if (sym.isCompileTimeOnly) { - def defaultMsg = - sm"""Reference to ${sym.fullLocationString} should not have survived past type checking, - |it should have been processed and eliminated during expansion of an enclosing macro.""" - // The getOrElse part should never happen, it's just here as a backstop. - ctx.error(sym.compileTimeOnlyMessage getOrElse defaultMsg, pos) - }*/ - } - - /** Check that a deprecated val or def does not override a - * concrete, non-deprecated method. If it does, then - * deprecation is meaningless. - */ - private def checkDeprecatedOvers(tree: Tree)(implicit ctx: Context): Unit = { - val symbol = tree.symbol - if (symbol.isDeprecated) { - val concrOvers = - symbol.allOverriddenSymbols.filter(sym => - !sym.isDeprecated && !sym.is(Deferred)) - if (!concrOvers.isEmpty) - ctx.deprecationWarning( - symbol.toString + " overrides concrete, non-deprecated symbol(s):" + - concrOvers.map(_.name.decode).mkString(" ", ", ", ""), tree.pos) - } - } - - /** Verify classes extending AnyVal meet the requirements */ - private def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = { - def checkValueClassMember(stat: Tree) = stat match { - case _: ValDef if !stat.symbol.is(ParamAccessor) => - ctx.error(s"value class may not define non-parameter field", stat.pos) - case _: DefDef if stat.symbol.isConstructor => - ctx.error(s"value class may not define secondary constructor", stat.pos) - case _: MemberDef | _: Import | EmptyTree => - // ok - case _ => - ctx.error(s"value class may not contain initialization statements", stat.pos) - } - if (isDerivedValueClass(clazz)) { - if (clazz.is(Trait)) - ctx.error("Only classes (not traits) are allowed to extend AnyVal", clazz.pos) - if (clazz.is(Abstract)) - ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos) - if (!clazz.isStatic) - ctx.error(s"value class may not be a ${if (clazz.owner.isTerm) "local class" else "member of another class"}", clazz.pos) - else { - val clParamAccessors = clazz.asClass.paramAccessors.filter(sym => sym.isTerm && !sym.is(Method)) - clParamAccessors match { - case List(param) => - if (param.is(Mutable)) - ctx.error("value class parameter must not be a var", param.pos) - case _ => - ctx.error("value class needs to have exactly one val parameter", clazz.pos) - } - } - stats.foreach(checkValueClassMember) - } - } - - type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)] - - class OptLevelInfo extends DotClass { - def levelAndIndex: LevelAndIndex = Map() - def enterReference(sym: Symbol, pos: Position): Unit = () - } - - /** A class to help in forward reference checking */ - class LevelInfo(outerLevelAndIndex: LevelAndIndex, stats: List[Tree])(implicit ctx: Context) - extends OptLevelInfo { - override val levelAndIndex: LevelAndIndex = - ((outerLevelAndIndex, 0) /: stats) {(mi, stat) => - val (m, idx) = mi - val m1 = stat match { - case stat: MemberDef => m.updated(stat.symbol, (this, idx)) - case _ => m - } - (m1, idx + 1) - }._1 - var maxIndex: Int = Int.MinValue - var refPos: Position = _ - var refSym: Symbol = _ - - override def enterReference(sym: Symbol, pos: Position): Unit = - if (sym.exists && sym.owner.isTerm) - levelAndIndex.get(sym) match { - case Some((level, idx)) if (level.maxIndex < idx) => - level.maxIndex = idx - level.refPos = pos - level.refSym = sym - case _ => - } - } - - val NoLevelInfo = new OptLevelInfo() -} -import RefChecks._ - -/** Post-attribution checking and transformation, which fulfills the following roles - * - * 1. This phase performs the following checks. - * - * - only one overloaded alternative defines default arguments - * - applyDynamic methods are not overloaded - * - all overrides conform to rules laid down by `checkAllOverrides`. - * - any value classes conform to rules laid down by `checkDerivedValueClass`. - * - this(...) constructor calls do not forward reference other definitions in their block (not even lazy vals). - * - no forward reference in a local block jumps over a non-lazy val definition. - * - a class and its companion object do not both define a class or module with the same name. - * - * 2. It warns about references to symbols labeled deprecated or migration. - - * 3. It performs the following transformations: - * - * - if (true) A else B --> A - * if (false) A else B --> B - * - macro definitions are eliminated. - * - * 4. It makes members not private where necessary. The following members - * cannot be private in the Java model: - * - term members of traits - * - the primary constructor of a value class - * - the parameter accessor of a value class - * - members accessed from an inner or companion class. - * All these members are marked as NotJavaPrivate. - * Unlike in Scala 2.x not-private members keep their name. It is - * up to the backend to find a unique expanded name for them. The - * rationale to do name changes that late is that they are very fragile. - - * todo: But RefChecks is not done yet. It's still a somewhat dirty port from the Scala 2 version. - * todo: move untrivial logic to their own mini-phases - */ -class RefChecks extends MiniPhase { thisTransformer => - - import tpd._ - - override def phaseName: String = "refchecks" - - val treeTransform = new Transform(NoLevelInfo) - - class Transform(currentLevel: RefChecks.OptLevelInfo = RefChecks.NoLevelInfo) extends TreeTransform { - def phase = thisTransformer - - override def prepareForStats(trees: List[Tree])(implicit ctx: Context) = { - // println(i"preparing for $trees%; %, owner = ${ctx.owner}") - if (ctx.owner.isTerm) new Transform(new LevelInfo(currentLevel.levelAndIndex, trees)) - else this - } - - override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees - - override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo) = { - checkDeprecatedOvers(tree) - val sym = tree.symbol - if (sym.exists && sym.owner.isTerm && !sym.is(Lazy)) - currentLevel.levelAndIndex.get(sym) match { - case Some((level, symIdx)) if symIdx < level.maxIndex => - ctx.debuglog("refsym = " + level.refSym) - ctx.error(s"forward reference extends over definition of $sym", level.refPos) - case _ => - } - tree - } - - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = { - checkDeprecatedOvers(tree) - if (tree.symbol is Macro) EmptyTree else tree - } - - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = try { - val cls = ctx.owner - checkOverloadedRestrictions(cls) - checkParents(cls) - checkCompanionNameClashes(cls) - checkAllOverrides(cls) - checkDerivedValueClass(cls, tree.body) - tree - } catch { - case ex: MergeError => - ctx.error(ex.getMessage, tree.pos) - tree - } - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { - checkUndesiredProperties(tree.symbol, tree.pos) - currentLevel.enterReference(tree.symbol, tree.pos) - tree - } - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = { - checkUndesiredProperties(tree.symbol, tree.pos) - tree - } - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = { - if (isSelfConstrCall(tree)) { - assert(currentLevel.isInstanceOf[LevelInfo], ctx.owner + "/" + i"$tree") - val level = currentLevel.asInstanceOf[LevelInfo] - if (level.maxIndex > 0) { - // An implementation restriction to avoid VerifyErrors and lazyvals mishaps; see SI-4717 - ctx.debuglog("refsym = " + level.refSym) - ctx.error("forward reference not allowed from self constructor invocation", level.refPos) - } - } - tree - } - - override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) = - tree.cond.tpe match { - case ConstantType(value) => if (value.booleanValue) tree.thenp else tree.elsep - case _ => tree - } - - override def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo) = { - currentLevel.enterReference(tree.tpe.typeSymbol, tree.pos) - tree - } - - override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { - tree.fun match { - case fun@Select(qual, selector) => - val sym = tree.symbol - - if (sym == defn.Any_isInstanceOf) { - val argType = tree.args.head.tpe - val qualCls = qual.tpe.widen.classSymbol - val argCls = argType.classSymbol - if (qualCls.isPrimitiveValueClass && !argCls.isPrimitiveValueClass) ctx.error("isInstanceOf cannot test if value types are references", tree.pos) - } - case _ => - } - tree - } - } -} - -/* todo: rewrite and re-enable - -// Comparison checking ------------------------------------------------------- - - object normalizeAll extends TypeMap { - def apply(tp: Type) = mapOver(tp).normalize - } - - def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint) (fn, args) match { - case (tap@TypeApply(fun, targs), List(view: ApplyImplicitView)) if fun.symbol == currentRun.runDefinitions.Option_apply => - unit.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567 - case _ => - } - - private def isObjectOrAnyComparisonMethod(sym: Symbol) = sym match { - case Object_eq | Object_ne | Object_== | Object_!= | Any_== | Any_!= => true - case _ => false - } - /** Check the sensibility of using the given `equals` to compare `qual` and `other`. */ - private def checkSensibleEquals(pos: Position, qual: Tree, name: Name, sym: Symbol, other: Tree) = { - def isReferenceOp = sym == Object_eq || sym == Object_ne - def isNew(tree: Tree) = tree match { - case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true - case _ => false - } - def underlyingClass(tp: Type): Symbol = { - val sym = tp.widen.typeSymbol - if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi) - else sym - } - val actual = underlyingClass(other.tpe) - val receiver = underlyingClass(qual.tpe) - def onTrees[T](f: List[Tree] => T) = f(List(qual, other)) - def onSyms[T](f: List[Symbol] => T) = f(List(receiver, actual)) - - // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol` - def typesString = normalizeAll(qual.tpe.widen)+" and " + normalizeAll(other.tpe.widen) - - /* Symbols which limit the warnings we can issue since they may be value types */ - val isMaybeValue = Set[Symbol](AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass) - - // Whether def equals(other: Any) has known behavior: it is the default - // inherited from java.lang.Object, or it is a synthetically generated - // case equals. TODO - more cases are warnable if the target is a synthetic - // equals. - def isUsingWarnableEquals = { - val m = receiver.info.member(nme.equals_) - ((m == Object_equals) || (m == Any_equals) || isMethodCaseEquals(m)) - } - def isMethodCaseEquals(m: Symbol) = m.isSynthetic && m.owner.isCase - def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_)) - // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere. - def isUsingDefaultScalaOp = sym == Object_== || sym == Object_!= || sym == Any_== || sym == Any_!= - def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual) - - // Whether the operands+operator represent a warnable combo (assuming anyrefs) - // Looking for comparisons performed with ==/!= in combination with either an - // equals method inherited from Object or a case class synthetic equals (for - // which we know the logic.) - def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) - def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info) - def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass - def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass - def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass - def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) - def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass - def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass - // includes java.lang.Number if appropriate [SI-5779] - def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s) - def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s) - // used to short-circuit unrelatedTypes check if both sides are special - def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) - val nullCount = onSyms(_ filter (_ == NullClass) size) - def isNonsenseValueClassCompare = ( - !haveSubclassRelationship - && isUsingDefaultScalaOp - && isEitherValueClass - && !isCaseEquals - ) - - // Have we already determined that the comparison is non-sensible? I mean, non-sensical? - var isNonSensible = false - - def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { - val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) - unit.warning(pos, s"comparing $what using `${name.decode}' will always yield $msg") - isNonSensible = true - } - def nonSensible(pre: String, alwaysEqual: Boolean) = - nonSensibleWarning(s"${pre}values of types $typesString", alwaysEqual) - def nonSensiblyEq() = nonSensible("", alwaysEqual = true) - def nonSensiblyNeq() = nonSensible("", alwaysEqual = false) - def nonSensiblyNew() = nonSensibleWarning("a fresh object", alwaysEqual = false) - - def unrelatedMsg = name match { - case nme.EQ | nme.eq => "never compare equal" - case _ => "always compare unequal" - } - def unrelatedTypes() = if (!isNonSensible) { - val weaselWord = if (isEitherValueClass) "" else " most likely" - unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") - } - - if (nullCount == 2) // null == null - nonSensiblyEq() - else if (nullCount == 1) { - if (onSyms(_ exists isPrimitiveValueClass)) // null == 5 - nonSensiblyNeq() - else if (onTrees( _ exists isNew)) // null == new AnyRef - nonSensiblyNew() - } - else if (isBoolean(receiver)) { - if (!isBoolean(actual) && !isMaybeValue(actual)) // true == 5 - nonSensiblyNeq() - } - else if (isUnit(receiver)) { - if (isUnit(actual)) // () == () - nonSensiblyEq() - else if (!isUnit(actual) && !isMaybeValue(actual)) // () == "abc" - nonSensiblyNeq() - } - else if (isNumeric(receiver)) { - if (!isNumeric(actual)) - if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc" - nonSensiblyNeq() - } - else if (isWarnable && !isCaseEquals) { - if (isNew(qual)) // new X == y - nonSensiblyNew() - else if (isNew(other) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y - nonSensiblyNew() - else if (receiver.isEffectivelyFinal && !(receiver isSubClass actual) && !actual.isRefinementClass) { // object X, Y; X == Y - if (isEitherNullable) - nonSensible("non-null ", false) - else - nonSensiblyNeq() - } - } - - // warn if one but not the other is a derived value class - // this is especially important to enable transitioning from - // regular to value classes without silent failures. - if (isNonsenseValueClassCompare) - unrelatedTypes() - // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean - else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { - // better to have lubbed and lost - def warnIfLubless(): Unit = { - val common = global.lub(List(actual.tpe, receiver.tpe)) - if (ObjectTpe <:< common) - unrelatedTypes() - } - // warn if actual has a case parent that is not same as receiver's; - // if actual is not a case, then warn if no common supertype, as below - if (isCaseEquals) { - def thisCase = receiver.info.member(nme.equals_).owner - actual.info.baseClasses.find(_.isCase) match { - case Some(p) if p != thisCase => nonSensible("case class ", false) - case None => - // stronger message on (Some(1) == None) - //if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq() - //else - // if a class, it must be super to thisCase (and receiver) since not <: thisCase - if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq() - else if (!haveSubclassRelationship) warnIfLubless() - case _ => - } - } - // warn only if they have no common supertype below Object - else if (!haveSubclassRelationship) { - warnIfLubless() - } - } - } - /** Sensibility check examines flavors of equals. */ - def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match { - case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) => - checkSensibleEquals(pos, qual, name, fn.symbol, args.head) - case _ => - } -*/ - -/* --------------- Overflow ------------------------------------------------- - * - - def accessFlagsToString(sym: Symbol) = flagsToString( - sym getFlag (PRIVATE | PROTECTED), - if (sym.hasAccessBoundary) "" + sym.privateWithin.name else "" - ) - - def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match { - case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => - rtp1 <:< rtp2 - case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => - rtp1 <:< rtp2 - case (TypeRef(_, sym, _), _) if sym.isModuleClass => - overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix) - case _ => - def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner) - - (tp1 <:< tp2) || ( // object override check - tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && { - val cb1 = classBoundAsSeen(tp1) - val cb2 = classBoundAsSeen(tp2) - (cb1 <:< cb2) && { - log("Allowing %s to override %s because %s <:< %s".format(tp1, tp2, cb1, cb2)) - true - } - } - ) - } - private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean)(implicit ctx: Context) = tp match { - case TypeRef(pre, sym, args) => - tree match { - case tt: TypeTree if tt.original == null => // SI-7783 don't warn about inferred types - // FIXME: reconcile this check with one in resetAttrs - case _ => checkUndesiredProperties(sym, tree.pos) - } - if (sym.isJavaDefined) - sym.typeParams foreach (_.cookJavaRawInfo()) - if (!tp.isHigherKinded && !skipBounds) - checkBounds(tree, pre, sym.owner, sym.typeParams, args) - case _ => - } - - private def checkTypeRefBounds(tp: Type, tree: Tree) = { - var skipBounds = false - tp match { - case AnnotatedType(ann :: Nil, underlying) if ann.symbol == UncheckedBoundsClass => - skipBounds = true - underlying - case TypeRef(pre, sym, args) => - if (!tp.isHigherKinded && !skipBounds) - checkBounds(tree, pre, sym.owner, sym.typeParams, args) - tp - case _ => - tp - } - } - - private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach { tp => - checkTypeRef(tp, tree, skipBounds = false) - checkTypeRefBounds(tp, tree) - } - private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f - - private def applyRefchecksToAnnotations(tree: Tree)(implicit ctx: Context): Unit = { - def applyChecks(annots: List[Annotation]) = { - checkAnnotations(annots map (_.atp), tree) - transformTrees(annots flatMap (_.args)) - } - - tree match { - case m: MemberDef => - val sym = m.symbol - applyChecks(sym.annotations) - // validate implicitNotFoundMessage - analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn => - unit.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn") - } - - case tpt@TypeTree() => - if (tpt.original != null) { - tpt.original foreach { - case dc@TypeTreeWithDeferredRefCheck() => - applyRefchecksToAnnotations(dc.check()) // #2416 - case _ => - } - } - - doTypeTraversal(tree) { - case tp @ AnnotatedType(annots, _) => - applyChecks(annots) - case tp => - } - case _ => - } - } - - private def transformCaseApply(tree: Tree, ifNot: => Unit) = { - val sym = tree.symbol - - def isClassTypeAccessible(tree: Tree): Boolean = tree match { - case TypeApply(fun, targs) => - isClassTypeAccessible(fun) - case Select(module, apply) => - ( // SI-4859 `CaseClass1().InnerCaseClass2()` must not be rewritten to `new InnerCaseClass2()`; - // {expr; Outer}.Inner() must not be rewritten to `new Outer.Inner()`. - treeInfo.isQualifierSafeToElide(module) && - // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case, - // the companion class is actually not a ClassSymbol, but a reference to an abstract type. - module.symbol.companionClass.isClass - ) - } - - val doTransform = - sym.isRealMethod && - sym.isCase && - sym.name == nme.apply && - isClassTypeAccessible(tree) - - if (doTransform) { - tree foreach { - case i@Ident(_) => - enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()` - case _ => - } - toConstructor(tree.pos, tree.tpe) - } - else { - ifNot - tree - } - } - - private def transformApply(tree: Apply): Tree = tree match { - case Apply( - Select(qual, nme.filter | nme.withFilter), - List(Function( - List(ValDef(_, pname, tpt, _)), - Match(_, CaseDef(pat1, _, _) :: _)))) - if ((pname startsWith nme.CHECK_IF_REFUTABLE_STRING) && - isIrrefutable(pat1, tpt.tpe) && (qual.tpe <:< tree.tpe)) => - - transform(qual) - - case Apply(fn, args) => - // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability - // analyses in the pattern matcher - if (!inPattern) { - checkImplicitViewOptionApply(tree.pos, fn, args) - checkSensible(tree.pos, fn, args) - } - currentApplication = tree - tree - } - private def transformSelect(tree: Select): Tree = { - val Select(qual, _) = tree - val sym = tree.symbol - - checkUndesiredProperties(sym, tree.pos) - checkDelayedInitSelect(qual, sym, tree.pos) - - if (!sym.exists) - devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe) - else if (sym.isLocalToThis) - varianceValidator.checkForEscape(sym, currentClass) - - def checkSuper(mix: Name) = - // term should have been eliminated by super accessors - assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY), (qual.symbol, sym, mix)) - - transformCaseApply(tree, - qual match { - case Super(_, mix) => checkSuper(mix) - case _ => - } - ) - } - private def transformIf(tree: If): Tree = { - val If(cond, thenpart, elsepart) = tree - def unitIfEmpty(t: Tree): Tree = - if (t == EmptyTree) Literal(Constant(())).setPos(tree.pos).setType(UnitTpe) else t - - cond.tpe match { - case ConstantType(value) => - val res = if (value.booleanValue) thenpart else elsepart - unitIfEmpty(res) - case _ => tree - } - } - - // Warning about nullary methods returning Unit. TODO: move to lint - private def checkNullaryMethodReturnType(sym: Symbol) = sym.tpe match { - case NullaryMethodType(restpe) if restpe.typeSymbol == UnitClass => - // this may be the implementation of e.g. a generic method being parameterized - // on Unit, in which case we had better let it slide. - val isOk = ( - sym.isGetter - || (sym.name containsName nme.DEFAULT_GETTER_STRING) - || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType)) - ) - if (!isOk) - unit.warning(sym.pos, s"side-effecting nullary methods are discouraged: suggest defining as `def ${sym.name.decode}()` instead") - case _ => () - } - - /* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */ - def toConstructor(pos: Position, tpe: Type)(implicit ctx: Context): Tree = { - val rtpe = tpe.finalResultType - assert(rtpe.typeSymbol.is(Case), tpe) - New(rtpe).withPos(pos).select(rtpe.typeSymbol.primaryConstructor) - } - private def isIrrefutable(pat: Tree, seltpe: Type): Boolean = pat match { - case Apply(_, args) => - val clazz = pat.tpe.typeSymbol - clazz == seltpe.typeSymbol && - clazz.isCaseClass && - (args corresponds clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable) - case Typed(pat, tpt) => - seltpe <:< tpt.tpe - case Ident(tpnme.WILDCARD) => - true - case Bind(_, pat) => - isIrrefutable(pat, seltpe) - case _ => - false - } - private def checkDelayedInitSelect(qual: Tree, sym: Symbol, pos: Position) = { - def isLikelyUninitialized = ( - (sym.owner isSubClass DelayedInitClass) - && !qual.tpe.isInstanceOf[ThisType] - && sym.accessedOrSelf.isVal - ) - if (settings.lint.value && isLikelyUninitialized) - unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value") - } - private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = ( - (otherSym != NoSymbol) - && !otherSym.isProtected - && !otherSym.isTypeParameterOrSkolem - && !otherSym.isExistentiallyBound - && (otherSym isLessAccessibleThan memberSym) - && (otherSym isLessAccessibleThan memberSym.enclClass) - ) - private def lessAccessibleSymsInType(other: Type, memberSym: Symbol): List[Symbol] = { - val extras = other match { - case TypeRef(pre, _, args) => - // checking the prefix here gives us spurious errors on e.g. a private[process] - // object which contains a type alias, which normalizes to a visible type. - args filterNot (_ eq NoPrefix) flatMap (tp => lessAccessibleSymsInType(tp, memberSym)) - case _ => - Nil - } - if (lessAccessible(other.typeSymbol, memberSym)) other.typeSymbol :: extras - else extras - } - private def warnLessAccessible(otherSym: Symbol, memberSym: Symbol) { - val comparison = accessFlagsToString(memberSym) match { - case "" => "" - case acc => " is " + acc + " but" - } - val cannot = - if (memberSym.isDeferred) "may be unable to provide a concrete implementation of" - else "may be unable to override" - - unit.warning(memberSym.pos, - "%s%s references %s %s.".format( - memberSym.fullLocationString, comparison, - accessFlagsToString(otherSym), otherSym - ) + "\nClasses which cannot access %s %s %s.".format( - otherSym.decodedName, cannot, memberSym.decodedName) - ) - } - - /** Warn about situations where a method signature will include a type which - * has more restrictive access than the method itself. - */ - private def checkAccessibilityOfReferencedTypes(tree: Tree) { - val member = tree.symbol - - def checkAccessibilityOfType(tpe: Type) { - val inaccessible = lessAccessibleSymsInType(tpe, member) - // if the unnormalized type is accessible, that's good enough - if (inaccessible.isEmpty) () - // or if the normalized type is, that's good too - else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) () - // otherwise warn about the inaccessible syms in the unnormalized type - else inaccessible foreach (sym => warnLessAccessible(sym, member)) - } - - // types of the value parameters - mapParamss(member)(p => checkAccessibilityOfType(p.tpe)) - // upper bounds of type parameters - member.typeParams.map(_.info.bounds.hi.widen) foreach checkAccessibilityOfType - } - - private def checkByNameRightAssociativeDef(tree: DefDef) { - tree match { - case DefDef(_, name, _, params :: _, _, _) => - if (settings.lint && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol))) - unit.warning(tree.pos, - "by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.") - case _ => - } - } - override def transform(tree: Tree)(implicit ctx: Context): Tree = { - //val savedLocalTyper = localTyper - try { - val sym = tree.symbol - checkOverloadedRestrictions(ctx.owner) - checkAllOverrides(ctx.owner) - checkAnyValSubclass(ctx.owner) - if (ctx.owner.isDerivedValueClass) - ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler! - tree - - - // Apply RefChecks to annotations. Makes sure the annotations conform to - // type bounds (bug #935), issues deprecation warnings for symbols used - // inside annotations. - // applyRefchecksToAnnotations(tree) ??? - var result: Tree = tree match { - case tree: ValOrDefDef => - // move to lint: - // if (settings.warnNullaryUnit) - // checkNullaryMethodReturnType(sym) - // if (settings.warnInaccessible) { - // if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic) - // checkAccessibilityOfReferencedTypes(tree) - // } - // tree match { - // case dd: DefDef => checkByNameRightAssociativeDef(dd) - // case _ => - // } - tree - - case Template(constr, parents, self, body) => - // localTyper = localTyper.atOwner(tree, currentOwner) - checkOverloadedRestrictions(ctx.owner) - checkAllOverrides(ctx.owner) - checkAnyValSubclass(ctx.owner) - if (ctx.owner.isDerivedValueClass) - ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler! - tree - - case tpt: TypeTree => - transform(tpt.original) - tree - - case TypeApply(fn, args) => - checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe)) - transformCaseApply(tree, ()) - - case x @ Apply(_, _) => - transformApply(x) - - case x @ If(_, _, _) => - transformIf(x) - - case New(tpt) => - enterReference(tree.pos, tpt.tpe.typeSymbol) - tree - - case treeInfo.WildcardStarArg(_) if !isRepeatedParamArg(tree) => - unit.error(tree.pos, "no `: _*' annotation allowed here\n" + - "(such annotations are only allowed in arguments to *-parameters)") - tree - - case Ident(name) => - checkUndesiredProperties(sym, tree.pos) - transformCaseApply(tree, - if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) { - assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug - enterReference(tree.pos, sym) - } - ) - - case x @ Select(_, _) => - transformSelect(x) - - case UnApply(fun, args) => - transform(fun) // just make sure we enterReference for unapply symbols, note that super.transform(tree) would not transform(fun) - // transformTrees(args) // TODO: is this necessary? could there be forward references in the args?? - // probably not, until we allow parameterised extractors - tree - - - case _ => tree - } - - // skip refchecks in patterns.... - result = result match { - case CaseDef(pat, guard, body) => - val pat1 = savingInPattern { - inPattern = true - transform(pat) - } - treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) - case LabelDef(_, _, _) if treeInfo.hasSynthCaseSymbol(result) => - savingInPattern { - inPattern = true - deriveLabelDef(result)(transform) - } - case Apply(fun, args) if fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol) => - savingInPattern { - // SI-7756 If we were in a translated pattern, we can now switch out of pattern mode, as the label apply signals - // that we are in the user-supplied code in the case body. - // - // Relies on the translation of: - // (null: Any) match { case x: List[_] => x; x.reverse; case _ => }' - // to: - // <synthetic> val x2: List[_] = (x1.asInstanceOf[List[_]]: List[_]); - // matchEnd4({ x2; x2.reverse}) // case body is an argument to a label apply. - inPattern = false - super.transform(result) - } - case ValDef(_, _, _, _) if treeInfo.hasSynthCaseSymbol(result) => - deriveValDef(result)(transform) // SI-7716 Don't refcheck the tpt of the synthetic val that holds the selector. - case _ => - super.transform(result) - } - result match { - case ClassDef(_, _, _, _) - | TypeDef(_, _, _, _) => - if (result.symbol.isLocalToBlock || result.symbol.isTopLevel) - varianceValidator.traverse(result) - case tt @ TypeTree() if tt.original != null => - varianceValidator.traverse(tt.original) // See SI-7872 - case _ => - } - - checkUnexpandedMacro(result) - - result - } catch { - case ex: TypeError => - if (settings.debug) ex.printStackTrace() - unit.error(tree.pos, ex.getMessage()) - tree - } finally { - localTyper = savedLocalTyper - currentApplication = savedCurrentApplication - } - } -*/ - diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala deleted file mode 100644 index ee2d68278..000000000 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ /dev/null @@ -1,524 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast._ -import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ -import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ -import TypeApplications.AppliedType -import util.Positions._ -import config.Printers.typr -import ast.Trees._ -import NameOps._ -import collection.mutable -import reporting.diagnostic.Message -import reporting.diagnostic.messages._ - -trait TypeAssigner { - import tpd._ - - /** The qualifying class of a this or super with prefix `qual` (which might be empty). - * @param packageOk The qualifier may refer to a package. - */ - def qualifyingClass(tree: untpd.Tree, qual: Name, packageOK: Boolean)(implicit ctx: Context): Symbol = { - def qualifies(sym: Symbol) = - sym.isClass && ( - qual.isEmpty || - sym.name == qual || - sym.is(Module) && sym.name.stripModuleClassSuffix == qual) - ctx.outersIterator.map(_.owner).find(qualifies) match { - case Some(c) if packageOK || !(c is Package) => - c - case _ => - ctx.error( - if (qual.isEmpty) tree.show + " can be used only in a class, object, or template" - else qual.show + " is not an enclosing class", tree.pos) - NoSymbol - } - } - - /** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`. - * Approximation steps are: - * - * - follow aliases and upper bounds if the original refers to a forbidden symbol - * - widen termrefs that refer to a forbidden symbol - * - replace ClassInfos of forbidden classes by the intersection of their parents, refined by all - * non-private fields, methods, and type members. - * - if the prefix of a class refers to a forbidden symbol, first try to replace the prefix, - * if this is not possible, replace the ClassInfo as above. - * - drop refinements referring to a forbidden symbol. - */ - def avoid(tp: Type, symsToAvoid: => List[Symbol])(implicit ctx: Context): Type = { - val widenMap = new TypeMap { - lazy val forbidden = symsToAvoid.toSet - def toAvoid(tp: Type): Boolean = - // TODO: measure the cost of using `existsPart`, and if necessary replace it - // by a `TypeAccumulator` where we have set `stopAtStatic = true`. - tp existsPart { - case tp: NamedType => forbidden contains tp.symbol - case tp: ThisType => forbidden contains tp.cls - case _ => false - } - def apply(tp: Type): Type = tp match { - case tp: TermRef - if toAvoid(tp) && (variance > 0 || tp.info.widenExpr <:< tp) => - // Can happen if `x: y.type`, then `x.type =:= y.type`, hence we can widen `x.type` - // to y.type in all contexts, not just covariant ones. - apply(tp.info.widenExpr) - case tp: TypeRef if toAvoid(tp) => - tp.info match { - case TypeAlias(ref) => - apply(ref) - case info: ClassInfo if variance > 0 => - if (!(forbidden contains tp.symbol)) { - val prefix = apply(tp.prefix) - val tp1 = tp.derivedSelect(prefix) - if (tp1.typeSymbol.exists) - return tp1 - } - val parentType = info.parentsWithArgs.reduceLeft(ctx.typeComparer.andType(_, _)) - def addRefinement(parent: Type, decl: Symbol) = { - val inherited = - parentType.findMember(decl.name, info.cls.thisType, Private) - .suchThat(decl.matches(_)) - val inheritedInfo = inherited.info - if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) { - val r = RefinedType(parent, decl.name, decl.info) - typr.println(i"add ref $parent $decl --> " + r) - r - } - else - parent - } - val refinableDecls = info.decls.filterNot( - sym => sym.is(TypeParamAccessor | Private) || sym.isConstructor) - val fullType = (parentType /: refinableDecls)(addRefinement) - mapOver(fullType) - case TypeBounds(lo, hi) if variance > 0 => - apply(hi) - case _ => - mapOver(tp) - } - case tp @ HKApply(tycon, args) if toAvoid(tycon) => - apply(tp.superType) - case tp @ AppliedType(tycon, args) if toAvoid(tycon) => - val base = apply(tycon) - var args = tp.baseArgInfos(base.typeSymbol) - if (base.typeParams.length != args.length) - args = base.typeParams.map(_.paramBounds) - apply(base.appliedTo(args)) - case tp @ RefinedType(parent, name, rinfo) if variance > 0 => - val parent1 = apply(tp.parent) - val refinedInfo1 = apply(rinfo) - if (toAvoid(refinedInfo1)) { - typr.println(s"dropping refinement from $tp") - if (name.isTypeName) tp.derivedRefinedType(parent1, name, TypeBounds.empty) - else parent1 - } else { - tp.derivedRefinedType(parent1, name, refinedInfo1) - } - case tp: TypeVar if ctx.typerState.constraint.contains(tp) => - val lo = ctx.typerState.constraint.fullLowerBound(tp.origin) - val lo1 = avoid(lo, symsToAvoid) - if (lo1 ne lo) lo1 else tp - case _ => - mapOver(tp) - } - } - widenMap(tp) - } - - def avoidingType(expr: Tree, bindings: List[Tree])(implicit ctx: Context): Type = - avoid(expr.tpe, localSyms(bindings).filter(_.isTerm)) - - def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = - Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass))) - - /** A denotation exists really if it exists and does not point to a stale symbol. */ - final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try - denot match { - case denot: SymDenotation => - denot.exists && { - denot.ensureCompleted - !denot.isAbsent - } - case denot: SingleDenotation => - val sym = denot.symbol - (sym eq NoSymbol) || reallyExists(sym.denot) - case _ => - true - } - catch { - case ex: StaleSymbol => false - } - - /** If `tpe` is a named type, check that its denotation is accessible in the - * current context. Return the type with those alternatives as denotations - * which are accessible. - * - * Also performs the following normalizations on the type `tpe`. - * (1) parameter accessors are always dereferenced. - * (2) if the owner of the denotation is a package object, it is assured - * that the package object shows up as the prefix. - */ - def ensureAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = { - def test(tpe: Type, firstTry: Boolean): Type = tpe match { - case tpe: NamedType => - val pre = tpe.prefix - val name = tpe.name - val d = tpe.denot.accessibleFrom(pre, superAccess) - if (!d.exists) { - // it could be that we found an inaccessible private member, but there is - // an inherited non-private member with the same name and signature. - val d2 = pre.nonPrivateMember(name) - if (reallyExists(d2) && firstTry) - test(tpe.shadowed.withDenot(d2), false) - else if (pre.derivesFrom(defn.DynamicClass)) { - TryDynamicCallType - } else { - val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) - val what = alts match { - case Nil => - name.toString - case sym :: Nil => - if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated - case _ => - em"none of the overloaded alternatives named $name" - } - val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" - val whyNot = new StringBuffer - alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot)) - if (!tpe.isError) - ctx.error(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos) - ErrorType - } - } - else if (d.symbol is TypeParamAccessor) - if (d.info.isAlias) - ensureAccessible(d.info.bounds.hi, superAccess, pos) - else // It's a named parameter, use the non-symbolic representation to pick up inherited versions as well - d.symbol.owner.thisType.select(d.symbol.name) - else - ctx.makePackageObjPrefixExplicit(tpe withDenot d) - case _ => - tpe - } - test(tpe, true) - } - - /** The type of a selection with `name` of a tree with type `site`. - */ - def selectionType(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = { - val mbr = site.member(name) - if (reallyExists(mbr)) site.select(name, mbr) - else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) { - TryDynamicCallType - } else { - if (!site.isErroneous) { - def kind = if (name.isTypeName) "type" else "value" - def addendum = - if (site.derivesFrom(defn.DynamicClass)) "\npossible cause: maybe a wrong Dynamic method signature?" - else "" - ctx.error( - if (name == nme.CONSTRUCTOR) ex"$site does not have a constructor" - else NotAMember(site, name, kind), - pos) - } - ErrorType - } - } - - /** The selection type, which is additionally checked for accessibility. - */ - def accessibleSelectionType(tree: untpd.RefTree, qual1: Tree)(implicit ctx: Context): Type = { - val ownType = selectionType(qual1.tpe.widenIfUnstable, tree.name, tree.pos) - ensureAccessible(ownType, qual1.isInstanceOf[Super], tree.pos) - } - - /** Type assignment method. Each method takes as parameters - * - an untpd.Tree to which it assigns a type, - * - typed child trees it needs to access to cpmpute that type, - * - any further information it needs to access to compute that type. - */ - - def assignType(tree: untpd.Ident, tp: Type)(implicit ctx: Context) = - tree.withType(tp) - - def assignType(tree: untpd.Select, qual: Tree)(implicit ctx: Context): Select = { - def qualType = qual.tpe.widen - def arrayElemType = { - val JavaArrayType(elemtp) = qualType - elemtp - } - val p = nme.primitive - val tp = tree.name match { - case p.arrayApply => MethodType(defn.IntType :: Nil, arrayElemType) - case p.arrayUpdate => MethodType(defn.IntType :: arrayElemType :: Nil, defn.UnitType) - case p.arrayLength => MethodType(Nil, defn.IntType) - - // Note that we do not need to handle calls to Array[T]#clone() specially: - // The JLS section 10.7 says "The return type of the clone method of an array type - // T[] is T[]", but the actual return type at the bytecode level is Object which - // is casted to T[] by javac. Since the return type of Array[T]#clone() is Array[T], - // this is exactly what Erasure will do. - - case _ => accessibleSelectionType(tree, qual) - } - tree.withType(tp) - } - - def assignType(tree: untpd.New, tpt: Tree)(implicit ctx: Context) = - tree.withType(tpt.tpe) - - def assignType(tree: untpd.Literal)(implicit ctx: Context) = - tree.withType { - val value = tree.const - value.tag match { - case UnitTag => defn.UnitType - case NullTag => defn.NullType - case _ => if (ctx.erasedTypes) value.tpe else ConstantType(value) - } - } - - def assignType(tree: untpd.This)(implicit ctx: Context) = { - val cls = qualifyingClass(tree, tree.qual.name, packageOK = false) - tree.withType(cls.thisType) - } - - def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context) = { - val mix = tree.mix - val qtype @ ThisType(_) = qual.tpe - val cls = qtype.cls - - def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix.name) match { - case p :: Nil => - p - case Nil => - errorType(em"$mix does not name a parent class of $cls", tree.pos) - case p :: q :: _ => - errorType("ambiguous parent class qualifier", tree.pos) - } - val owntype = - if (mixinClass.exists) mixinClass.typeRef - else if (!mix.isEmpty) findMixinSuper(cls.info) - else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent - else { - val ps = cls.classInfo.parentsWithArgs - if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y) - } - tree.withType(SuperType(cls.thisType, owntype)) - } - - def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { - val ownType = fn.tpe.widen match { - case fntpe @ MethodType(_, ptypes) => - if (sameLength(ptypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) - else wrongNumberOfArgs(fn.tpe, "", fntpe.typeParams, args, tree.pos) - case t => - errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos) - } - tree.withType(ownType) - } - - def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { - val ownType = fn.tpe.widen match { - case pt: PolyType => - val paramNames = pt.paramNames - if (hasNamedArg(args)) { - // Type arguments which are specified by name (immutable after this first loop) - val namedArgMap = new mutable.HashMap[Name, Type] - for (NamedArg(name, arg) <- args) - if (namedArgMap.contains(name)) - ctx.error("duplicate name", arg.pos) - else if (!paramNames.contains(name)) - ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos) - else - namedArgMap(name) = arg.tpe - - // Holds indexes of non-named typed arguments in paramNames - val gapBuf = new mutable.ListBuffer[Int] - def nextPoly(idx: Int) = { - val newIndex = gapBuf.length - gapBuf += idx - // Re-index unassigned type arguments that remain after transformation - PolyParam(pt, newIndex) - } - - // Type parameters after naming assignment, conserving paramNames order - val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) => - namedArgMap.getOrElse(pname, nextPoly(idx)) - } - - val transform = new TypeMap { - def apply(t: Type) = t match { - case PolyParam(`pt`, idx) => normArgs(idx) - case _ => mapOver(t) - } - } - val resultType1 = transform(pt.resultType) - if (gapBuf.isEmpty) resultType1 - else { - val gaps = gapBuf.toList - pt.derivedPolyType( - gaps.map(paramNames), - gaps.map(idx => transform(pt.paramBounds(idx)).bounds), - resultType1) - } - } - else { - val argTypes = args.tpes - if (sameLength(argTypes, paramNames) || ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes) - else wrongNumberOfArgs(fn.tpe, "type", pt.typeParams, args, tree.pos) - } - case _ => - errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos) - } - - tree.withType(ownType) - } - - def assignType(tree: untpd.Typed, tpt: Tree)(implicit ctx: Context) = - tree.withType(tpt.tpe) - - def assignType(tree: untpd.NamedArg, arg: Tree)(implicit ctx: Context) = - tree.withType(arg.tpe) - - def assignType(tree: untpd.Assign)(implicit ctx: Context) = - tree.withType(defn.UnitType) - - def assignType(tree: untpd.Block, stats: List[Tree], expr: Tree)(implicit ctx: Context) = - tree.withType(avoidingType(expr, stats)) - - def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(implicit ctx: Context) = - tree.withType(avoidingType(expansion, bindings)) - - def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = - tree.withType(thenp.tpe | elsep.tpe) - - def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = - tree.withType( - if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length) - else target.tpe) - - def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) = - tree.withType(body.tpe) - - def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = - tree.withType(ctx.typeComparer.lub(cases.tpes)) - - def assignType(tree: untpd.Return)(implicit ctx: Context) = - tree.withType(defn.NothingType) - - def assignType(tree: untpd.Try, expr: Tree, cases: List[CaseDef])(implicit ctx: Context) = - if (cases.isEmpty) tree.withType(expr.tpe) - else tree.withType(ctx.typeComparer.lub(expr.tpe :: cases.tpes)) - - def assignType(tree: untpd.SeqLiteral, elems: List[Tree], elemtpt: Tree)(implicit ctx: Context) = { - val ownType = tree match { - case tree: untpd.JavaSeqLiteral => defn.ArrayOf(elemtpt.tpe) - case _ => if (ctx.erasedTypes) defn.SeqType else defn.SeqType.appliedTo(elemtpt.tpe) - } - tree.withType(ownType) - } - - def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = - tree.withType(ref.tpe) - - def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = - tree.withType(left.tpe & right.tpe) - - def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = - tree.withType(left.tpe | right.tpe) - - /** Assign type of RefinedType. - * Refinements are typed as if they were members of refinement class `refineCls`. - */ - def assignType(tree: untpd.RefinedTypeTree, parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context) = { - def addRefinement(parent: Type, refinement: Tree): Type = { - val rsym = refinement.symbol - val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info - RefinedType(parent, rsym.name, rinfo) - } - val refined = (parent.tpe /: refinements)(addRefinement) - tree.withType(RecType.closeOver(rt => refined.substThis(refineCls, RecThis(rt)))) - } - - def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { - val tparams = tycon.tpe.typeParams - lazy val ntparams = tycon.tpe.namedTypeParams - def refineNamed(tycon: Type, arg: Tree) = arg match { - case ast.Trees.NamedArg(name, argtpt) => - // Dotty deviation: importing ast.Trees._ and matching on NamedArg gives a cyclic ref error - val tparam = tparams.find(_.paramName == name) match { - case Some(tparam) => tparam - case none => ntparams.find(_.name == name).getOrElse(NoSymbol) - } - if (tparam.isTypeParam) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam)) - else errorType(i"$tycon does not have a parameter or abstract type member named $name", arg.pos) - case _ => - errorType(s"named and positional type arguments may not be mixed", arg.pos) - } - val ownType = - if (hasNamedArg(args)) (tycon.tpe /: args)(refineNamed) - else if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) - else wrongNumberOfArgs(tycon.tpe, "type", tparams, args, tree.pos) - tree.withType(ownType) - } - - def assignType(tree: untpd.PolyTypeTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) = - tree.withType(body.tpe.LambdaAbstract(tparamDefs.map(_.symbol))) - - def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) = - tree.withType(ExprType(result.tpe)) - - def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) = - tree.withType(if (lo eq hi) TypeAlias(lo.tpe) else TypeBounds(lo.tpe, hi.tpe)) - - def assignType(tree: untpd.Bind, sym: Symbol)(implicit ctx: Context) = - tree.withType(NamedType.withFixedSym(NoPrefix, sym)) - - def assignType(tree: untpd.Alternative, trees: List[Tree])(implicit ctx: Context) = - tree.withType(ctx.typeComparer.lub(trees.tpes)) - - def assignType(tree: untpd.UnApply, proto: Type)(implicit ctx: Context) = - tree.withType(proto) - - def assignType(tree: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = - tree.withType(if (sym.exists) assertExists(symbolicIfNeeded(sym).orElse(sym.valRef)) else NoType) - - def assignType(tree: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = - tree.withType(symbolicIfNeeded(sym).orElse(sym.termRefWithSig)) - - def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = - tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef)) - - private def symbolicIfNeeded(sym: Symbol)(implicit ctx: Context) = { - val owner = sym.owner - owner.infoOrCompleter match { - case info: ClassInfo if info.givenSelfType.exists => - // In that case a simple typeRef/termWithWithSig could return a member of - // the self type, not the symbol itself. To avoid this, we make the reference - // symbolic. In general it seems to be faster to keep the non-symblic - // reference, since there is less pressure on the uniqueness tables that way - // and less work to update all the different references. That's why symbolic references - // are only used if necessary. - NamedType.withFixedSym(owner.thisType, sym) - case _ => NoType - } - } - - def assertExists(tp: Type) = { assert(tp != NoType); tp } - - def assignType(tree: untpd.Import, sym: Symbol)(implicit ctx: Context) = - tree.withType(sym.nonMemberTermRef) - - def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(implicit ctx: Context) = - tree.withType(AnnotatedType(arg.tpe.widen, Annotation(annot))) - - def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) = - tree.withType(pid.symbol.valRef) -} - -object TypeAssigner extends TypeAssigner - diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala deleted file mode 100644 index 64936e106..000000000 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ /dev/null @@ -1,1952 +0,0 @@ -package dotty.tools -package dotc -package typer - -import core._ -import ast._ -import Trees._ -import Constants._ -import StdNames._ -import Scopes._ -import Denotations._ -import ProtoTypes._ -import Contexts._ -import Comments._ -import Symbols._ -import Types._ -import SymDenotations._ -import Annotations._ -import Names._ -import NameOps._ -import Flags._ -import Decorators._ -import ErrorReporting._ -import Checking._ -import Inferencing._ -import EtaExpansion.etaExpand -import dotty.tools.dotc.transform.Erasure.Boxing -import util.Positions._ -import util.common._ -import util.SourcePosition -import collection.mutable -import annotation.tailrec -import Implicits._ -import util.Stats.{track, record} -import config.Printers.{typr, gadts} -import rewrite.Rewrites.patch -import NavigateAST._ -import transform.SymUtils._ -import language.implicitConversions -import printing.SyntaxHighlighting._ - -object Typer { - - /** The precedence of bindings which determines which of several bindings will be - * accessed by an Ident. - */ - object BindingPrec { - val definition = 4 - val namedImport = 3 - val wildImport = 2 - val packageClause = 1 - val nothingBound = 0 - def isImportPrec(prec: Int) = prec == namedImport || prec == wildImport - } - - /** Assert tree has a position, unless it is empty or a typed splice */ - def assertPositioned(tree: untpd.Tree)(implicit ctx: Context) = - if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) - assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}") -} - -class Typer extends Namer with TypeAssigner with Applications with Implicits with Dynamic with Checking with Docstrings { - - import Typer._ - import tpd.{cpy => _, _} - import untpd.cpy - import Dynamic.isDynamicMethod - import reporting.diagnostic.Message - import reporting.diagnostic.messages._ - - /** A temporary data item valid for a single typed ident: - * The set of all root import symbols that have been - * encountered as a qualifier of an import so far. - * Note: It would be more proper to move importedFromRoot into typedIdent. - * We should check that this has no performance degradation, however. - */ - private var importedFromRoot: Set[Symbol] = Set() - - /** Temporary data item for single call to typed ident: - * This symbol would be found under Scala2 mode, but is not - * in dotty (because dotty conforms to spec section 2 - * wrt to package member resolution but scalac doe not). - */ - private var foundUnderScala2: Type = NoType - - def newLikeThis: Typer = new Typer - - /** Attribute an identifier consisting of a simple name or wildcard - * - * @param tree The tree representing the identifier. - * Transformations: (1) Prefix class members with this. - * (2) Change imported symbols to selections. - * (3) Change pattern Idents id (but not wildcards) to id @ _ - */ - def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = track("typedIdent") { - val refctx = ctx - val name = tree.name - val noImports = ctx.mode.is(Mode.InPackageClauseName) - - /** Method is necessary because error messages need to bind to - * to typedIdent's context which is lost in nested calls to findRef - */ - def error(msg: => Message, pos: Position) = ctx.error(msg, pos) - - /** Is this import a root import that has been shadowed by an explicit - * import in the same program? - */ - def isDisabled(imp: ImportInfo, site: Type): Boolean = { - if (imp.isRootImport && (importedFromRoot contains site.termSymbol)) return true - if (imp.hiddenRoot.exists) importedFromRoot += imp.hiddenRoot - false - } - - /** Does this identifier appear as a constructor of a pattern? */ - def isPatternConstr = - if (ctx.mode.isExpr && (ctx.outer.mode is Mode.Pattern)) - ctx.outer.tree match { - case Apply(`tree`, _) => true - case _ => false - } - else false - - /** A symbol qualifies if it really exists. In addition, - * if we are in a constructor of a pattern, we ignore all definitions - * which are methods and not accessors (note: if we don't do that - * case x :: xs in class List would return the :: method). - */ - def qualifies(denot: Denotation): Boolean = - reallyExists(denot) && !( - pt.isInstanceOf[UnapplySelectionProto] && - (denot.symbol is (Method, butNot = Accessor))) - - /** Find the denotation of enclosing `name` in given context `ctx`. - * @param previous A denotation that was found in a more deeply nested scope, - * or else `NoDenotation` if nothing was found yet. - * @param prevPrec The binding precedence of the previous denotation, - * or else `nothingBound` if nothing was found yet. - * @param prevCtx The context of the previous denotation, - * or else `NoContext` if nothing was found yet. - */ - def findRef(previous: Type, prevPrec: Int, prevCtx: Context)(implicit ctx: Context): Type = { - import BindingPrec._ - - /** A string which explains how something was bound; Depending on `prec` this is either - * imported by <tree> - * or defined in <symbol> - */ - def bindingString(prec: Int, whereFound: Context, qualifier: String = "") = - if (prec == wildImport || prec == namedImport) { - ex"""imported$qualifier by ${hl"${whereFound.importInfo.toString}"}""" - } else - ex"""defined$qualifier in ${hl"${whereFound.owner.toString}"}""" - - /** Check that any previously found result from an inner context - * does properly shadow the new one from an outer context. - * @param found The newly found result - * @param newPrec Its precedence - * @param scala2pkg Special mode where we check members of the same package, but defined - * in different compilation units under Scala2. If set, and the - * previous and new contexts do not have the same scope, we select - * the previous (inner) definition. This models what scalac does. - */ - def checkNewOrShadowed(found: Type, newPrec: Int, scala2pkg: Boolean = false)(implicit ctx: Context): Type = - if (!previous.exists || ctx.typeComparer.isSameRef(previous, found)) found - else if ((prevCtx.scope eq ctx.scope) && - (newPrec == definition || - newPrec == namedImport && prevPrec == wildImport)) { - // special cases: definitions beat imports, and named imports beat - // wildcard imports, provided both are in contexts with same scope - found - } - else { - if (!scala2pkg && !previous.isError && !found.isError) { - error( - ex"""|reference to `$name` is ambiguous - |it is both ${bindingString(newPrec, ctx, "")} - |and ${bindingString(prevPrec, prevCtx, " subsequently")}""", - tree.pos) - } - previous - } - - /** The type representing a named import with enclosing name when imported - * from given `site` and `selectors`. - */ - def namedImportRef(site: Type, selectors: List[untpd.Tree])(implicit ctx: Context): Type = { - def checkUnambiguous(found: Type) = { - val other = namedImportRef(site, selectors.tail) - if (other.exists && found.exists && (found != other)) - error(em"reference to `$name` is ambiguous; it is imported twice in ${ctx.tree}", - tree.pos) - found - } - val Name = name.toTermName.decode - selectors match { - case selector :: rest => - selector match { - case Thicket(fromId :: Ident(Name) :: _) => - val Ident(from) = fromId - val selName = if (name.isTypeName) from.toTypeName else from - // Pass refctx so that any errors are reported in the context of the - // reference instead of the context of the import. - checkUnambiguous(selectionType(site, selName, tree.pos)(refctx)) - case Ident(Name) => - checkUnambiguous(selectionType(site, name, tree.pos)(refctx)) - case _ => - namedImportRef(site, rest) - } - case nil => - NoType - } - } - - /** The type representing a wildcard import with enclosing name when imported - * from given import info - */ - def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type = { - if (imp.isWildcardImport) { - val pre = imp.site - if (!isDisabled(imp, pre) && !(imp.excluded contains name.toTermName) && name != nme.CONSTRUCTOR) { - val denot = pre.member(name).accessibleFrom(pre)(refctx) - if (reallyExists(denot)) return pre.select(name, denot) - } - } - NoType - } - - /** Is (some alternative of) the given predenotation `denot` - * defined in current compilation unit? - */ - def isDefinedInCurrentUnit(denot: Denotation)(implicit ctx: Context): Boolean = denot match { - case MultiDenotation(d1, d2) => isDefinedInCurrentUnit(d1) || isDefinedInCurrentUnit(d2) - case denot: SingleDenotation => denot.symbol.sourceFile == ctx.source.file - } - - /** Is `denot` the denotation of a self symbol? */ - def isSelfDenot(denot: Denotation)(implicit ctx: Context) = denot match { - case denot: SymDenotation => denot is SelfName - case _ => false - } - - /** Would import of kind `prec` be not shadowed by a nested higher-precedence definition? */ - def isPossibleImport(prec: Int)(implicit ctx: Context) = - !noImports && - (prevPrec < prec || prevPrec == prec && (prevCtx.scope eq ctx.scope)) - - @tailrec def loop(implicit ctx: Context): Type = { - if (ctx.scope == null) previous - else { - val outer = ctx.outer - var result: Type = NoType - - // find definition - if ((ctx.scope ne outer.scope) || (ctx.owner ne outer.owner)) { - val defDenot = ctx.denotNamed(name) - if (qualifies(defDenot)) { - val curOwner = ctx.owner - val found = - if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType - else curOwner.thisType.select(name, defDenot) - if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot)) - result = checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry - else { - if (ctx.scala2Mode && !foundUnderScala2.exists) - foundUnderScala2 = checkNewOrShadowed(found, definition, scala2pkg = true) - if (defDenot.symbol is Package) - result = checkNewOrShadowed(previous orElse found, packageClause) - else if (prevPrec < packageClause) - result = findRef(found, packageClause, ctx)(outer) - } - } - } - - if (result.exists) result - else { // find import - val curImport = ctx.importInfo - if (ctx.owner.is(Package) && curImport != null && curImport.isRootImport && previous.exists) - previous // no more conflicts possible in this case - else if (isPossibleImport(namedImport) && (curImport ne outer.importInfo) && !curImport.sym.isCompleting) { - val namedImp = namedImportRef(curImport.site, curImport.selectors) - if (namedImp.exists) - findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer) - else if (isPossibleImport(wildImport)) { - val wildImp = wildImportRef(curImport) - if (wildImp.exists) - findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer) - else loop(outer) - } - else loop(outer) - } - else loop(outer) - } - } - } - - loop - } - - // begin typedIdent - def kind = if (name.isTermName) "" else "type " - typr.println(s"typed ident $kind$name in ${ctx.owner}") - if (ctx.mode is Mode.Pattern) { - if (name == nme.WILDCARD) - return tree.withType(pt) - if (isVarPattern(tree) && name.isTermName) - return typed(desugar.patternVar(tree), pt) - } - - - val rawType = { - val saved1 = importedFromRoot - val saved2 = foundUnderScala2 - importedFromRoot = Set.empty - foundUnderScala2 = NoType - try { - var found = findRef(NoType, BindingPrec.nothingBound, NoContext) - if (foundUnderScala2.exists && !(foundUnderScala2 =:= found)) { - ctx.migrationWarning( - ex"""Name resolution will change. - | currently selected : $foundUnderScala2 - | in the future, without -language:Scala2: $found""", tree.pos) - found = foundUnderScala2 - } - found - } - finally { - importedFromRoot = saved1 - foundUnderScala2 = saved2 - } - } - - val ownType = - if (rawType.exists) - ensureAccessible(rawType, superAccess = false, tree.pos) - else { - error(new MissingIdent(tree, kind, name.show), tree.pos) - ErrorType - } - - val tree1 = ownType match { - case ownType: NamedType if !prefixIsElidable(ownType) => - ref(ownType).withPos(tree.pos) - case _ => - tree.withType(ownType) - } - - checkValue(tree1, pt) - } - - private def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(implicit ctx: Context): Select = - healNonvariant( - checkValue(assignType(cpy.Select(tree)(qual, tree.name), qual), pt), - pt) - - /** Let `tree = p.n` where `p: T`. If tree's type is an unsafe instantiation - * (see TypeOps#asSeenFrom for how this can happen), rewrite the prefix `p` - * to `(p: <unknown skolem of type T>)` and try again with the new (stable) - * prefix. If the result has another unsafe instantiation, raise an error. - */ - private def healNonvariant[T <: Tree](tree: T, pt: Type)(implicit ctx: Context): T = - if (ctx.unsafeNonvariant == ctx.runId && tree.tpe.widen.hasUnsafeNonvariant) - tree match { - case tree @ Select(qual, _) if !qual.tpe.isStable => - val alt = typedSelect(tree, pt, Typed(qual, TypeTree(SkolemType(qual.tpe.widen)))) - typr.println(i"healed type: ${tree.tpe} --> $alt") - alt.asInstanceOf[T] - case _ => - ctx.error(ex"unsafe instantiation of type ${tree.tpe}", tree.pos) - tree - } - else tree - - def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { - def typeSelectOnTerm(implicit ctx: Context): Tree = { - val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) - if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) - val select = typedSelect(tree, pt, qual1) - if (select.tpe ne TryDynamicCallType) select - else if (pt.isInstanceOf[PolyProto] || pt.isInstanceOf[FunProto] || pt == AssignProto) select - else typedDynamicSelect(tree, Nil, pt) - } - - def typeSelectOnType(qual: untpd.Tree)(implicit ctx: Context) = - typedSelect(untpd.cpy.Select(tree)(qual, tree.name.toTypeName), pt) - - def tryJavaSelectOnType(implicit ctx: Context): Tree = tree.qualifier match { - case Select(qual, name) => typeSelectOnType(untpd.Select(qual, name.toTypeName)) - case Ident(name) => typeSelectOnType(untpd.Ident(name.toTypeName)) - case _ => errorTree(tree, "cannot convert to type selection") // will never be printed due to fallback - } - - def selectWithFallback(fallBack: Context => Tree) = - tryAlternatively(typeSelectOnTerm(_))(fallBack) - - if (tree.qualifier.isType) { - val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this)) - assignType(cpy.Select(tree)(qual1, tree.name), qual1) - } - else if (ctx.compilationUnit.isJava && tree.name.isTypeName) - // SI-3120 Java uses the same syntax, A.B, to express selection from the - // value A and from the type A. We have to try both. - selectWithFallback(tryJavaSelectOnType(_)) // !!! possibly exponential bcs of qualifier retyping - else if (tree.name == nme.withFilter && tree.getAttachment(desugar.MaybeFilter).isDefined) - selectWithFallback { - implicit ctx => - typedSelect(untpd.cpy.Select(tree)(tree.qualifier, nme.filter), pt) // !!! possibly exponential bcs of qualifier retyping - } - else - typeSelectOnTerm(ctx) - } - - def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = track("typedThis") { - assignType(tree) - } - - def typedSuper(tree: untpd.Super, pt: Type)(implicit ctx: Context): Tree = track("typedSuper") { - val qual1 = typed(tree.qual) - val inConstrCall = pt match { - case pt: SelectionProto if pt.name == nme.CONSTRUCTOR => true - case _ => false - } - pt match { - case pt: SelectionProto if pt.name.isTypeName => - qual1 // don't do super references for types; they are meaningless anyway - case _ => - assignType(cpy.Super(tree)(qual1, tree.mix), qual1, inConstrCall) - } - } - - def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") { - assignType(tree) - } - - def typedNew(tree: untpd.New, pt: Type)(implicit ctx: Context) = track("typedNew") { - tree.tpt match { - case templ: untpd.Template => - import untpd._ - val x = tpnme.ANON_CLASS - val clsDef = TypeDef(x, templ).withFlags(Final) - typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) - case _ => - var tpt1 = typedType(tree.tpt) - tpt1 = tpt1.withType(ensureAccessible(tpt1.tpe, superAccess = false, tpt1.pos)) - tpt1.tpe.dealias match { - case TypeApplications.EtaExpansion(tycon) => tpt1 = tpt1.withType(tycon) - case _ => - } - checkClassType(tpt1.tpe, tpt1.pos, traitReq = false, stablePrefixReq = true) - - tpt1 match { - case AppliedTypeTree(_, targs) => - for (targ @ TypeBoundsTree(_, _) <- targs) - ctx.error("type argument must be fully defined", targ.pos) - case _ => - } - - assignType(cpy.New(tree)(tpt1), tpt1) - // todo in a later phase: checkInstantiatable(cls, tpt1.pos) - } - } - - def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = track("typedTyped") { - /* Handles three cases: - * @param ifPat how to handle a pattern (_: T) - * @param ifExpr how to handle an expression (e: T) - * @param wildName what name `w` to use in the rewriting of - * (x: T) to (x @ (w: T)). This is either `_` or `_*`. - */ - def cases(ifPat: => Tree, ifExpr: => Tree, wildName: TermName) = tree.expr match { - case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) => - if (id.name == nme.WILDCARD || id.name == nme.WILDCARD_STAR) ifPat - else { - import untpd._ - typed(Bind(id.name, Typed(Ident(wildName), tree.tpt)).withPos(id.pos), pt) - } - case _ => ifExpr - } - def ascription(tpt: Tree, isWildcard: Boolean) = { - val underlyingTreeTpe = - if (isRepeatedParamType(tpt)) TypeTree(defn.SeqType.appliedTo(pt :: Nil)) - else tpt - - val expr1 = - if (isRepeatedParamType(tpt)) tree.expr.withType(defn.SeqType.appliedTo(pt :: Nil)) - else if (isWildcard) tree.expr.withType(tpt.tpe) - else typed(tree.expr, tpt.tpe.widenSkolem) - assignType(cpy.Typed(tree)(expr1, tpt), underlyingTreeTpe) - } - if (untpd.isWildcardStarArg(tree)) - cases( - ifPat = ascription(TypeTree(defn.RepeatedParamType.appliedTo(pt)), isWildcard = true), - ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType)), - wildName = nme.WILDCARD_STAR) - else { - def typedTpt = checkSimpleKinded(typedType(tree.tpt)) - def handlePattern: Tree = { - val tpt1 = typedTpt - // special case for an abstract type that comes with a class tag - tpt1.tpe.dealias match { - case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper => - inferImplicit(defn.ClassTagType.appliedTo(tref), - EmptyTree, tpt1.pos)(ctx.retractMode(Mode.Pattern)) match { - case SearchSuccess(arg, _, _) => - return typed(untpd.Apply(untpd.TypedSplice(arg), tree.expr), pt) - case _ => - } - case _ => - if (!ctx.isAfterTyper) tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible)) - } - ascription(tpt1, isWildcard = true) - } - cases( - ifPat = handlePattern, - ifExpr = ascription(typedTpt, isWildcard = false), - wildName = nme.WILDCARD) - } - } - - def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = track("typedNamedArg") { - val arg1 = typed(tree.arg, pt) - assignType(cpy.NamedArg(tree)(tree.name, arg1), arg1) - } - - def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context) = track("typedAssign") { - tree.lhs match { - case lhs @ Apply(fn, args) => - typed(cpy.Apply(lhs)(untpd.Select(fn, nme.update), args :+ tree.rhs), pt) - case untpd.TypedSplice(Apply(MaybePoly(Select(fn, app), targs), args)) if app == nme.apply => - val rawUpdate: untpd.Tree = untpd.Select(untpd.TypedSplice(fn), nme.update) - val wrappedUpdate = - if (targs.isEmpty) rawUpdate - else untpd.TypeApply(rawUpdate, targs map (untpd.TypedSplice(_))) - val appliedUpdate = cpy.Apply(fn)(wrappedUpdate, (args map (untpd.TypedSplice(_))) :+ tree.rhs) - typed(appliedUpdate, pt) - case lhs => - val lhsCore = typedUnadapted(lhs, AssignProto) - def lhs1 = typed(untpd.TypedSplice(lhsCore)) - def canAssign(sym: Symbol) = // allow assignments from the primary constructor to class fields - sym.is(Mutable, butNot = Accessor) || - ctx.owner.isPrimaryConstructor && !sym.is(Method) && sym.owner == ctx.owner.owner || - ctx.owner.name.isTraitSetterName || ctx.owner.isStaticConstructor - lhsCore.tpe match { - case ref: TermRef if canAssign(ref.symbol) => - assignType(cpy.Assign(tree)(lhs1, typed(tree.rhs, ref.info))) - case _ => - def reassignmentToVal = - errorTree(cpy.Assign(tree)(lhsCore, typed(tree.rhs, lhs1.tpe.widen)), - "reassignment to val") - lhsCore.tpe match { - case ref: TermRef => // todo: further conditions to impose on getter? - val pre = ref.prefix - val setterName = ref.name.setterName - val setter = pre.member(setterName) - lhsCore match { - case lhsCore: RefTree if setter.exists => - val setterTypeRaw = pre.select(setterName, setter) - val setterType = ensureAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos) - val lhs2 = healNonvariant( - untpd.rename(lhsCore, setterName).withType(setterType), WildcardType) - typedUnadapted(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil)) - case _ => - reassignmentToVal - } - case TryDynamicCallType => - typedDynamicAssign(tree, pt) - case tpe => - reassignmentToVal - } - } - } - } - - def typedBlockStats(stats: List[untpd.Tree])(implicit ctx: Context): (Context, List[tpd.Tree]) = - (index(stats), typedStats(stats, ctx.owner)) - - def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") { - val (exprCtx, stats1) = typedBlockStats(tree.stats) - val ept = - if (tree.isInstanceOf[untpd.InfixOpBlock]) - // Right-binding infix operations are expanded to InfixBlocks, which may be followed by arguments. - // Example: `(a /: bs)(op)` expands to `{ val x = a; bs./:(x) } (op)` where `{...}` is an InfixBlock. - pt - else pt.notApplied - val expr1 = typedExpr(tree.expr, ept)(exprCtx) - ensureNoLocalRefs( - assignType(cpy.Block(tree)(stats1, expr1), stats1, expr1), pt, localSyms(stats1)) - } - - def escapingRefs(block: Tree, localSyms: => List[Symbol])(implicit ctx: Context): collection.Set[NamedType] = { - lazy val locals = localSyms.toSet - block.tpe namedPartsWith (tp => locals.contains(tp.symbol)) - } - - /** Check that expression's type can be expressed without references to locally defined - * symbols. The following two remedies are tried before giving up: - * 1. If the expected type of the expression is fully defined, pick it as the - * type of the result expressed by adding a type ascription. - * 2. If (1) fails, force all type variables so that the block's type is - * fully defined and try again. - */ - protected def ensureNoLocalRefs(tree: Tree, pt: Type, localSyms: => List[Symbol], forcedDefined: Boolean = false)(implicit ctx: Context): Tree = { - def ascribeType(tree: Tree, pt: Type): Tree = tree match { - case block @ Block(stats, expr) => - val expr1 = ascribeType(expr, pt) - cpy.Block(block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant - case _ => - Typed(tree, TypeTree(pt.simplified)) - } - val leaks = escapingRefs(tree, localSyms) - if (leaks.isEmpty) tree - else if (isFullyDefined(pt, ForceDegree.none)) ascribeType(tree, pt) - else if (!forcedDefined) { - fullyDefinedType(tree.tpe, "block", tree.pos) - val tree1 = ascribeType(tree, avoid(tree.tpe, localSyms)) - ensureNoLocalRefs(tree1, pt, localSyms, forcedDefined = true) - } else - errorTree(tree, - em"local definition of ${leaks.head.name} escapes as part of expression's type ${tree.tpe}"/*; full type: ${result.tpe.toString}"*/) - } - - def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree = track("typedIf") { - val cond1 = typed(tree.cond, defn.BooleanType) - val thenp1 = typed(tree.thenp, pt.notApplied) - val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied) - val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil) - assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) - } - - private def decomposeProtoFunction(pt: Type, defaultArity: Int)(implicit ctx: Context): (List[Type], Type) = pt match { - case _ if defn.isFunctionType(pt) => - // if expected parameter type(s) are wildcards, approximate from below. - // if expected result type is a wildcard, approximate from above. - // this can type the greatest set of admissible closures. - (pt.dealias.argTypesLo.init, pt.dealias.argTypesHi.last) - case SAMType(meth) => - val mt @ MethodType(_, paramTypes) = meth.info - (paramTypes, mt.resultType) - case _ => - (List.range(0, defaultArity) map alwaysWildcardType, WildcardType) - } - - def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { - val untpd.Function(args, body) = tree - if (ctx.mode is Mode.Type) - typed(cpy.AppliedTypeTree(tree)( - untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt) - else { - val params = args.asInstanceOf[List[untpd.ValDef]] - - pt match { - case pt: TypeVar if untpd.isFunctionWithUnknownParamType(tree) => - // try to instantiate `pt` if this is possible. If it does not - // work the error will be reported later in `inferredParam`, - // when we try to infer the parameter type. - isFullyDefined(pt, ForceDegree.noBottom) - case _ => - } - - val (protoFormals, protoResult) = decomposeProtoFunction(pt, params.length) - - def refersTo(arg: untpd.Tree, param: untpd.ValDef): Boolean = arg match { - case Ident(name) => name == param.name - case _ => false - } - - /** The function body to be returned in the closure. Can become a TypedSplice - * of a typed expression if this is necessary to infer a parameter type. - */ - var fnBody = tree.body - - /** If function is of the form - * (x1, ..., xN) => f(x1, ..., XN) - * the type of `f`, otherwise NoType. (updates `fnBody` as a side effect). - */ - def calleeType: Type = fnBody match { - case Apply(expr, args) if (args corresponds params)(refersTo) => - expr match { - case untpd.TypedSplice(expr1) => - expr1.tpe - case _ => - val protoArgs = args map (_ withType WildcardType) - val callProto = FunProto(protoArgs, WildcardType, this) - val expr1 = typedExpr(expr, callProto) - fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args) - expr1.tpe - } - case _ => - NoType - } - - /** Two attempts: First, if expected type is fully defined pick this one. - * Second, if function is of the form - * (x1, ..., xN) => f(x1, ..., XN) - * and f has a method type MT, pick the corresponding parameter type in MT, - * if this one is fully defined. - * If both attempts fail, issue a "missing parameter type" error. - */ - def inferredParamType(param: untpd.ValDef, formal: Type): Type = { - if (isFullyDefined(formal, ForceDegree.noBottom)) return formal - calleeType.widen match { - case mtpe: MethodType => - val pos = params indexWhere (_.name == param.name) - if (pos < mtpe.paramTypes.length) { - val ptype = mtpe.paramTypes(pos) - if (isFullyDefined(ptype, ForceDegree.noBottom)) return ptype - } - case _ => - } - val ofFun = - if (nme.syntheticParamNames(args.length + 1) contains param.name) - i" of expanded function $tree" - else - "" - errorType(i"missing parameter type for parameter ${param.name}$ofFun, expected = $pt", param.pos) - } - - def protoFormal(i: Int): Type = - if (protoFormals.length == params.length) protoFormals(i) - else errorType(i"wrong number of parameters, expected: ${protoFormals.length}", tree.pos) - - /** Is `formal` a product type which is elementwise compatible with `params`? */ - def ptIsCorrectProduct(formal: Type) = { - val pclass = defn.ProductNType(params.length).symbol - isFullyDefined(formal, ForceDegree.noBottom) && - formal.derivesFrom(pclass) && - formal.baseArgTypes(pclass).corresponds(params) { - (argType, param) => - param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe - } - } - - val desugared = - if (protoFormals.length == 1 && params.length != 1 && ptIsCorrectProduct(protoFormals.head)) { - desugar.makeTupledFunction(params, fnBody) - } - else { - val inferredParams: List[untpd.ValDef] = - for ((param, i) <- params.zipWithIndex) yield - if (!param.tpt.isEmpty) param - else cpy.ValDef(param)( - tpt = untpd.TypeTree( - inferredParamType(param, protoFormal(i)).underlyingIfRepeated(isJava = false))) - - // Define result type of closure as the expected type, thereby pushing - // down any implicit searches. We do this even if the expected type is not fully - // defined, which is a bit of a hack. But it's needed to make the following work - // (see typers.scala and printers/PlainPrinter.scala for examples). - // - // def double(x: Char): String = s"$x$x" - // "abc" flatMap double - // - val resultTpt = protoResult match { - case WildcardType(_) => untpd.TypeTree() - case _ => untpd.TypeTree(protoResult) - } - val inlineable = pt.hasAnnotation(defn.InlineParamAnnot) - desugar.makeClosure(inferredParams, fnBody, resultTpt, inlineable) - } - typed(desugared, pt) - } - } - - def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context): Tree = track("typedClosure") { - val env1 = tree.env mapconserve (typed(_)) - val meth1 = typedUnadapted(tree.meth) - val target = - if (tree.tpt.isEmpty) - meth1.tpe.widen match { - case mt: MethodType => - pt match { - case SAMType(meth) if !defn.isFunctionType(pt) && mt <:< meth.info => - if (!isFullyDefined(pt, ForceDegree.all)) - ctx.error(ex"result type of closure is an underspecified SAM type $pt", tree.pos) - TypeTree(pt) - case _ => - if (!mt.isDependent) EmptyTree - else throw new java.lang.Error(i"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}, raw type = ${mt.toString}") // !!! DEBUG. Eventually, convert to an error? - } - case tp => - throw new java.lang.Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}") - } - else typed(tree.tpt) - //println(i"typing closure $tree : ${meth1.tpe.widen}") - assignType(cpy.Closure(tree)(env1, meth1, target), meth1, target) - } - - def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") { - tree.selector match { - case EmptyTree => - val (protoFormals, _) = decomposeProtoFunction(pt, 1) - val unchecked = pt <:< defn.PartialFunctionType - typed(desugar.makeCaseLambda(tree.cases, protoFormals.length, unchecked) withPos tree.pos, pt) - case _ => - val sel1 = typedExpr(tree.selector) - val selType = widenForMatchSelector( - fullyDefinedType(sel1.tpe, "pattern selector", tree.pos)) - - val cases1 = typedCases(tree.cases, selType, pt.notApplied) - val cases2 = harmonize(cases1).asInstanceOf[List[CaseDef]] - assignType(cpy.Match(tree)(sel1, cases2), cases2) - } - } - - def typedCases(cases: List[untpd.CaseDef], selType: Type, pt: Type)(implicit ctx: Context) = { - - /** gadtSyms = "all type parameters of enclosing methods that appear - * non-variantly in the selector type" todo: should typevars - * which appear with variances +1 and -1 (in different - * places) be considered as well? - */ - val gadtSyms: Set[Symbol] = ctx.traceIndented(i"GADT syms of $selType", gadts) { - val accu = new TypeAccumulator[Set[Symbol]] { - def apply(tsyms: Set[Symbol], t: Type): Set[Symbol] = { - val tsyms1 = t match { - case tr: TypeRef if (tr.symbol is TypeParam) && tr.symbol.owner.isTerm && variance == 0 => - tsyms + tr.symbol - case _ => - tsyms - } - foldOver(tsyms1, t) - } - } - accu(Set.empty, selType) - } - - cases mapconserve (typedCase(_, pt, selType, gadtSyms)) - } - - /** Type a case. Overridden in ReTyper, that's why it's separate from - * typedCases. - */ - def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") { - val originalCtx = ctx - - /** - replace all references to symbols associated with wildcards by their GADT bounds - * - enter all symbols introduced by a Bind in current scope - */ - val indexPattern = new TreeMap { - val elimWildcardSym = new TypeMap { - def apply(t: Type) = t match { - case ref @ TypeRef(_, tpnme.WILDCARD) if ctx.gadt.bounds.contains(ref.symbol) => - ctx.gadt.bounds(ref.symbol) - case TypeAlias(ref @ TypeRef(_, tpnme.WILDCARD)) if ctx.gadt.bounds.contains(ref.symbol) => - ctx.gadt.bounds(ref.symbol) - case _ => - mapOver(t) - } - } - override def transform(trt: Tree)(implicit ctx: Context) = - super.transform(trt.withType(elimWildcardSym(trt.tpe))) match { - case b: Bind => - if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol) - else ctx.error(new DuplicateBind(b, tree), b.pos) - b.symbol.info = elimWildcardSym(b.symbol.info) - b - case t => t - } - } - - def caseRest(pat: Tree)(implicit ctx: Context) = { - val pat1 = indexPattern.transform(pat) - val guard1 = typedExpr(tree.guard, defn.BooleanType) - val body1 = ensureNoLocalRefs(typedExpr(tree.body, pt), pt, ctx.scope.toList) - .ensureConforms(pt)(originalCtx) // insert a cast if body does not conform to expected type if we disregard gadt bounds - assignType(cpy.CaseDef(tree)(pat1, guard1, body1), body1) - } - - val gadtCtx = - if (gadtSyms.isEmpty) ctx - else { - val c = ctx.fresh.setFreshGADTBounds - for (sym <- gadtSyms) - if (!c.gadt.bounds.contains(sym)) - c.gadt.setBounds(sym, TypeBounds.empty) - c - } - val pat1 = typedPattern(tree.pat, selType)(gadtCtx) - caseRest(pat1)(gadtCtx.fresh.setNewScope) - } - - def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = track("typedReturn") { - def returnProto(owner: Symbol, locals: Scope): Type = - if (owner.isConstructor) defn.UnitType - else owner.info match { - case info: PolyType => - val tparams = locals.toList.takeWhile(_ is TypeParam) - assert(info.paramNames.length == tparams.length, - i"return mismatch from $owner, tparams = $tparams, locals = ${locals.toList}%, %") - info.instantiate(tparams.map(_.typeRef)).finalResultType - case info => - info.finalResultType - } - def enclMethInfo(cx: Context): (Tree, Type) = { - val owner = cx.owner - if (cx == NoContext || owner.isType) { - ctx.error("return outside method definition", tree.pos) - (EmptyTree, WildcardType) - } - else if (owner != cx.outer.owner && owner.isRealMethod) { - if (owner.isInlineMethod) - (EmptyTree, errorType(em"no explicit return allowed from inline $owner", tree.pos)) - else if (!owner.isCompleted) - (EmptyTree, errorType(em"$owner has return statement; needs result type", tree.pos)) - else { - val from = Ident(TermRef(NoPrefix, owner.asTerm)) - val proto = returnProto(owner, cx.scope) - (from, proto) - } - } - else enclMethInfo(cx.outer) - } - val (from, proto) = - if (tree.from.isEmpty) enclMethInfo(ctx) - else { - val from = tree.from.asInstanceOf[tpd.Tree] - val proto = - if (ctx.erasedTypes) from.symbol.info.finalResultType - else WildcardType // We cannot reliably detect the internal type view of polymorphic or dependent methods - // because we do not know the internal type params and method params. - // Hence no adaptation is possible, and we assume WildcardType as prototype. - (from, proto) - } - val expr1 = typedExpr(tree.expr orElse untpd.unitLiteral.withPos(tree.pos), proto) - assignType(cpy.Return(tree)(expr1, from)) - } - - def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") { - val expr1 = typed(tree.expr, pt.notApplied) - val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied) - val finalizer1 = typed(tree.finalizer, defn.UnitType) - val expr2 :: cases2x = harmonize(expr1 :: cases1) - val cases2 = cases2x.asInstanceOf[List[CaseDef]] - assignType(cpy.Try(tree)(expr2, cases2, finalizer1), expr2, cases2) - } - - def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Tree = track("typedThrow") { - val expr1 = typed(tree.expr, defn.ThrowableType) - Throw(expr1).withPos(tree.pos) - } - - def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context): SeqLiteral = track("typedSeqLiteral") { - val proto1 = pt.elemType match { - case NoType => WildcardType - case bounds: TypeBounds => WildcardType(bounds) - case elemtp => elemtp - } - val elems1 = tree.elems mapconserve (typed(_, proto1)) - val proto2 = // the computed type of the `elemtpt` field - if (!tree.elemtpt.isEmpty) WildcardType - else if (isFullyDefined(proto1, ForceDegree.none)) proto1 - else if (tree.elems.isEmpty && tree.isInstanceOf[Trees.JavaSeqLiteral[_]]) - defn.ObjectType // generic empty Java varargs are of type Object[] - else ctx.typeComparer.lub(elems1.tpes) - val elemtpt1 = typed(tree.elemtpt, proto2) - assignType(cpy.SeqLiteral(tree)(elems1, elemtpt1), elems1, elemtpt1) - } - - def typedInlined(tree: untpd.Inlined, pt: Type)(implicit ctx: Context): Inlined = { - val (exprCtx, bindings1) = typedBlockStats(tree.bindings) - val expansion1 = typed(tree.expansion, pt)(inlineContext(tree.call)(exprCtx)) - assignType(cpy.Inlined(tree)(tree.call, bindings1.asInstanceOf[List[MemberDef]], expansion1), - bindings1, expansion1) - } - - def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { - tree match { - case tree: untpd.DerivedTypeTree => - tree.ensureCompletions - try - TypeTree(tree.derivedType(tree.attachment(untpd.OriginalSymbol))) withPos tree.pos - // btw, no need to remove the attachment. The typed - // tree is different from the untyped one, so the - // untyped tree is no longer accessed after all - // accesses with typedTypeTree are done. - catch { - case ex: NoSuchElementException => - println(s"missing OriginalSymbol for ${ctx.owner.ownersIterator.toList}") - throw ex - } - case _ => - assert(isFullyDefined(pt, ForceDegree.none)) - tree.withType(pt) - } - } - - def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { - val ref1 = typedExpr(tree.ref) - checkStable(ref1.tpe, tree.pos) - assignType(cpy.SingletonTypeTree(tree)(ref1), ref1) - } - - def typedAndTypeTree(tree: untpd.AndTypeTree)(implicit ctx: Context): AndTypeTree = track("typedAndTypeTree") { - val left1 = typed(tree.left) - val right1 = typed(tree.right) - assignType(cpy.AndTypeTree(tree)(left1, right1), left1, right1) - } - - def typedOrTypeTree(tree: untpd.OrTypeTree)(implicit ctx: Context): OrTypeTree = track("typedOrTypeTree") { - val where = "in a union type" - val left1 = checkNotSingleton(typed(tree.left), where) - val right1 = checkNotSingleton(typed(tree.right), where) - assignType(cpy.OrTypeTree(tree)(left1, right1), left1, right1) - } - - def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(implicit ctx: Context): RefinedTypeTree = track("typedRefinedTypeTree") { - val tpt1 = if (tree.tpt.isEmpty) TypeTree(defn.ObjectType) else typedAheadType(tree.tpt) - val refineClsDef = desugar.refinedTypeToClass(tpt1, tree.refinements) - val refineCls = createSymbol(refineClsDef).asClass - val TypeDef(_, impl: Template) = typed(refineClsDef) - val refinements1 = impl.body - assert(tree.refinements.length == refinements1.length, s"${tree.refinements} != $refinements1") - val seen = mutable.Set[Symbol]() - for (refinement <- refinements1) { // TODO: get clarity whether we want to enforce these conditions - typr.println(s"adding refinement $refinement") - checkRefinementNonCyclic(refinement, refineCls, seen) - val rsym = refinement.symbol - if (rsym.is(Method) && rsym.allOverriddenSymbols.isEmpty) - ctx.error(i"refinement $rsym without matching type in parent $tpt1", refinement.pos) - } - assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls) - } - - def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): Tree = track("typedAppliedTypeTree") { - val tpt1 = typed(tree.tpt, AnyTypeConstructorProto)(ctx.retractMode(Mode.Pattern)) - val tparams = tpt1.tpe.typeParams - if (tparams.isEmpty) { - ctx.error(ex"${tpt1.tpe} does not take type parameters", tree.pos) - tpt1 - } - else { - var args = tree.args - val args1 = - if (hasNamedArg(args)) typedNamedArgs(args) - else { - if (args.length != tparams.length) { - wrongNumberOfArgs(tpt1.tpe, "type", tparams, args, tree.pos) - args = args.take(tparams.length) - } - def typedArg(arg: untpd.Tree, tparam: TypeParamInfo) = { - val (desugaredArg, argPt) = - if (ctx.mode is Mode.Pattern) - (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramBounds) - else - (arg, WildcardType) - typed(desugaredArg, argPt) - } - args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] - } - // check that arguments conform to bounds is done in phase PostTyper - assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) - } - } - - def typedPolyTypeTree(tree: untpd.PolyTypeTree)(implicit ctx: Context): Tree = track("typedPolyTypeTree") { - val PolyTypeTree(tparams, body) = tree - index(tparams) - val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef]) - val body1 = typedType(tree.body) - assignType(cpy.PolyTypeTree(tree)(tparams1, body1), tparams1, body1) - } - - def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = track("typedByNameTypeTree") { - val result1 = typed(tree.result) - assignType(cpy.ByNameTypeTree(tree)(result1), result1) - } - - /** Define a new symbol associated with a Bind or pattern wildcard and - * make it gadt narrowable. - */ - private def newPatternBoundSym(name: Name, info: Type, pos: Position)(implicit ctx: Context) = { - val flags = if (name.isTypeName) BindDefinedType else EmptyFlags - val sym = ctx.newSymbol(ctx.owner, name, flags | Case, info, coord = pos) - if (name.isTypeName) ctx.gadt.setBounds(sym, info.bounds) - sym - } - - def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = track("typedTypeBoundsTree") { - val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree) - val lo1 = typed(lo) - val hi1 = typed(hi) - val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1) - if (ctx.mode.is(Mode.Pattern)) { - // Associate a pattern-bound type symbol with the wildcard. - // The bounds of the type symbol can be constrained when comparing a pattern type - // with an expected type in typedTyped. The type symbol is eliminated once - // the enclosing pattern has been typechecked; see `indexPattern` in `typedCase`. - val wildcardSym = newPatternBoundSym(tpnme.WILDCARD, tree1.tpe, tree.pos) - tree1.withType(wildcardSym.typeRef) - } - else tree1 - } - - def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Tree = track("typedBind") { - val pt1 = fullyDefinedType(pt, "pattern variable", tree.pos) - val body1 = typed(tree.body, pt1) - typr.println(i"typed bind $tree pt = $pt1 bodytpe = ${body1.tpe}") - body1 match { - case UnApply(fn, Nil, arg :: Nil) if tree.body.isInstanceOf[untpd.Typed] => - // A typed pattern `x @ (_: T)` with an implicit `ctag: ClassTag[T]` - // was rewritten to `x @ ctag(_)`. - // Rewrite further to `ctag(x @ _)` - assert(fn.symbol.owner == defn.ClassTagClass) - tpd.cpy.UnApply(body1)(fn, Nil, - typed(untpd.Bind(tree.name, arg).withPos(tree.pos), arg.tpe) :: Nil) - case _ => - val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos) - assignType(cpy.Bind(tree)(tree.name, body1), sym) - } - } - - def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = track("typedAlternative") { - val trees1 = tree.trees mapconserve (typed(_, pt)) - assignType(cpy.Alternative(tree)(trees1), trees1) - } - - def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = { - // necessary to force annotation trees to be computed. - sym.annotations.foreach(_.ensureCompleted) - val annotCtx = ctx.outersIterator.dropWhile(_.owner == sym).next - // necessary in order to mark the typed ahead annotations as definitely typed: - untpd.modsDeco(mdef).mods.annotations.foreach(typedAnnotation(_)(annotCtx)) - } - - def typedAnnotation(annot: untpd.Tree)(implicit ctx: Context): Tree = track("typedAnnotation") { - typed(annot, defn.AnnotationType) - } - - def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = track("typedValDef") { - val ValDef(name, tpt, _) = vdef - completeAnnotations(vdef, sym) - val tpt1 = checkSimpleKinded(typedType(tpt)) - val rhs1 = vdef.rhs match { - case rhs @ Ident(nme.WILDCARD) => rhs withType tpt1.tpe - case rhs => typedExpr(rhs, tpt1.tpe) - } - val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym) - if (sym.is(Inline, butNot = DeferredOrParamAccessor)) - checkInlineConformant(rhs1, em"right-hand side of inline $sym") - patchIfLazy(vdef1) - vdef1 - } - - /** Add a @volitile to lazy vals when rewriting from Scala2 */ - private def patchIfLazy(vdef: ValDef)(implicit ctx: Context): Unit = { - val sym = vdef.symbol - if (sym.is(Lazy, butNot = Deferred | Module | Synthetic) && !sym.isVolatile && - ctx.scala2Mode && ctx.settings.rewrite.value.isDefined && - !ctx.isAfterTyper) - patch(Position(toUntyped(vdef).pos.start), "@volatile ") - } - - def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") { - val DefDef(name, tparams, vparamss, tpt, _) = ddef - completeAnnotations(ddef, sym) - val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) - val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef]) - if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1) - var tpt1 = checkSimpleKinded(typedType(tpt)) - - var rhsCtx = ctx - if (sym.isConstructor && !sym.isPrimaryConstructor && tparams1.nonEmpty) { - // for secondary constructors we need a context that "knows" - // that their type parameters are aliases of the class type parameters. - // See pos/i941.scala - rhsCtx = ctx.fresh.setFreshGADTBounds - (tparams1, sym.owner.typeParams).zipped.foreach ((tdef, tparam) => - rhsCtx.gadt.setBounds(tdef.symbol, TypeAlias(tparam.typeRef))) - } - val rhs1 = typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx) - - // Overwrite inline body to make sure it is not evaluated twice - if (sym.isInlineMethod) Inliner.registerInlineInfo(sym, _ => rhs1) - - if (sym.isAnonymousFunction) { - // If we define an anonymous function, make sure the return type does not - // refer to parameters. This is necessary because closure types are - // function types so no dependencies on parameters are allowed. - tpt1 = tpt1.withType(avoid(tpt1.tpe, vparamss1.flatMap(_.map(_.symbol)))) - } - - assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym) - //todo: make sure dependent method types do not depend on implicits or by-name params - } - - def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context): Tree = track("typedTypeDef") { - val TypeDef(name, rhs) = tdef - completeAnnotations(tdef, sym) - val rhs1 = tdef.rhs match { - case rhs @ PolyTypeTree(tparams, body) => - val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]] - val body1 = typedType(body) - assignType(cpy.PolyTypeTree(rhs)(tparams1, body1), tparams1, body1) - case rhs => - typedType(rhs) - } - assignType(cpy.TypeDef(tdef)(name, rhs1), sym) - } - - def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") { - val TypeDef(name, impl @ Template(constr, parents, self, _)) = cdef - val superCtx = ctx.superCallContext - - /** If `ref` is an implicitly parameterized trait, pass an implicit argument list. - * Otherwise, if `ref` is a parameterized trait, error. - * Note: Traits and classes currently always have at least an empty parameter list () - * before the implicit parameters (this is inserted if not given in source). - * We skip this parameter list when deciding whether a trait is parameterless or not. - * @param ref The tree referring to the (parent) trait - * @param psym Its type symbol - * @param cinfo The info of its constructor - */ - def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo match { - case cinfo: PolyType => - maybeCall(ref, psym, cinfo.resultType) - case cinfo @ MethodType(Nil, _) if cinfo.resultType.isInstanceOf[ImplicitMethodType] => - val icall = New(ref).select(nme.CONSTRUCTOR).appliedToNone - typedExpr(untpd.TypedSplice(icall))(superCtx) - case cinfo @ MethodType(Nil, _) if !cinfo.resultType.isInstanceOf[MethodType] => - ref - case cinfo: MethodType => - if (!ctx.erasedTypes) { // after constructors arguments are passed in super call. - typr.println(i"constr type: $cinfo") - ctx.error(em"parameterized $psym lacks argument list", ref.pos) - } - ref - case _ => - ref - } - - def typedParent(tree: untpd.Tree): Tree = - if (tree.isType) { - val result = typedType(tree)(superCtx) - val psym = result.tpe.typeSymbol - if (psym.is(Trait) && !cls.is(Trait) && !cls.superClass.isSubClass(psym)) - maybeCall(result, psym, psym.primaryConstructor.info) - else - result - } - else { - val result = typedExpr(tree)(superCtx) - checkParentCall(result, cls) - result - } - - completeAnnotations(cdef, cls) - val constr1 = typed(constr).asInstanceOf[DefDef] - val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.pos.toSynthetic) - val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx) - val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible - val dummy = localDummy(cls, impl) - val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol)) - - // Expand comments and type usecases - cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope) - - checkNoDoubleDefs(cls) - val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) - .withType(dummy.nonMemberTermRef) - checkVariance(impl1) - if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos) - val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls) - if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) { - val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass)) - ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true, - cls, isRequired, cdef.pos) - } - cdef1 - - // todo later: check that - // 1. If class is non-abstract, it is instantiatable: - // - self type is s supertype of own type - // - all type members have consistent bounds - // 2. all private type members have consistent bounds - // 3. Types do not override classes. - // 4. Polymorphic type defs override nothing. - } - - /** Ensure that the first type in a list of parent types Ps points to a non-trait class. - * If that's not already the case, add one. The added class type CT is determined as follows. - * First, let C be the unique class such that - * - there is a parent P_i such that P_i derives from C, and - * - for every class D: If some parent P_j, j <= i derives from D, then C derives from D. - * Then, let CT be the smallest type which - * - has C as its class symbol, and - * - for all parents P_i: If P_i derives from C then P_i <:< CT. - */ - def ensureFirstIsClass(parents: List[Type])(implicit ctx: Context): List[Type] = { - def realClassParent(cls: Symbol): ClassSymbol = - if (!cls.isClass) defn.ObjectClass - else if (!(cls is Trait)) cls.asClass - else cls.asClass.classParents match { - case parentRef :: _ => realClassParent(parentRef.symbol) - case nil => defn.ObjectClass - } - def improve(candidate: ClassSymbol, parent: Type): ClassSymbol = { - val pcls = realClassParent(parent.classSymbol) - if (pcls derivesFrom candidate) pcls else candidate - } - parents match { - case p :: _ if p.classSymbol.isRealClass => parents - case _ => - val pcls = (defn.ObjectClass /: parents)(improve) - typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseTypeWithArgs pcls)}%, %") - val ptype = ctx.typeComparer.glb( - defn.ObjectType :: (parents map (_ baseTypeWithArgs pcls))) - ptype :: parents - } - } - - /** Ensure that first parent tree refers to a real class. */ - def ensureFirstIsClass(parents: List[Tree], pos: Position)(implicit ctx: Context): List[Tree] = parents match { - case p :: ps if p.tpe.classSymbol.isRealClass => parents - case _ => - // add synthetic class type - val first :: _ = ensureFirstIsClass(parents.tpes) - TypeTree(checkFeasible(first, pos, em"\n in inferred parent $first")).withPos(pos) :: parents - } - - /** If this is a real class, make sure its first parent is a - * constructor call. Cannot simply use a type. Overridden in ReTyper. - */ - def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = { - val firstParent :: otherParents = parents - if (firstParent.isType && !(cls is Trait) && !cls.is(JavaDefined)) - typed(untpd.New(untpd.TypedSplice(firstParent), Nil)) :: otherParents - else parents - } - - /** Overridden in retyper */ - def checkVariance(tree: Tree)(implicit ctx: Context) = VarianceChecker.check(tree) - - def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context): Symbol = - ctx.newLocalDummy(cls, impl.pos) - - def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") { - val expr1 = typedExpr(imp.expr, AnySelectionProto) - checkStable(expr1.tpe, imp.expr.pos) - if (!ctx.isAfterTyper) checkRealizable(expr1.tpe, imp.expr.pos) - assignType(cpy.Import(imp)(expr1, imp.selectors), sym) - } - - def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") { - val pid1 = typedExpr(tree.pid, AnySelectionProto)(ctx.addMode(Mode.InPackageClauseName)) - val pkg = pid1.symbol - - // Package will not exist if a duplicate type has already been entered, see - // `tests/neg/1708.scala`, else branch's error message should be supressed - if (pkg.exists) { - val packageContext = - if (pkg is Package) ctx.fresh.setOwner(pkg.moduleClass).setTree(tree) - else { - ctx.error(em"$pkg is already defined, cannot be a package", tree.pos) - ctx - } - val stats1 = typedStats(tree.stats, pkg.moduleClass)(packageContext) - cpy.PackageDef(tree)(pid1.asInstanceOf[RefTree], stats1) withType pkg.valRef - } else errorTree(tree, i"package ${tree.pid.name} does not exist") - } - - def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") { - val annot1 = typedExpr(tree.annot, defn.AnnotationType) - val arg1 = typed(tree.arg, pt) - if (ctx.mode is Mode.Type) - assignType(cpy.Annotated(tree)(arg1, annot1), arg1, annot1) - else { - val tpt = TypeTree(AnnotatedType(arg1.tpe.widen, Annotation(annot1))) - assignType(cpy.Typed(tree)(arg1, tpt), tpt) - } - } - - def typedTypedSplice(tree: untpd.TypedSplice)(implicit ctx: Context): Tree = - tree.tree match { - case tree1: TypeTree => tree1 // no change owner necessary here ... - case tree1: Ident => tree1 // ... or here, since these trees cannot contain bindings - case tree1 => - if (ctx.owner ne tree.owner) tree1.changeOwner(tree.owner, ctx.owner) - else tree1 - } - - - def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(implicit ctx: Context): Tree = { - val untpd.PostfixOp(qual, nme.WILDCARD) = tree - val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto - var res = typed(qual, pt1) - if (pt1.eq(AnyFunctionProto) && !defn.isFunctionClass(res.tpe.classSymbol)) { - def msg = i"not a function: ${res.tpe}; cannot be followed by `_'" - if (ctx.scala2Mode) { - // Under -rewrite, patch `x _` to `(() => x)` - ctx.migrationWarning(msg, tree.pos) - patch(Position(tree.pos.start), "(() => ") - patch(Position(qual.pos.end, tree.pos.end), ")") - res = typed(untpd.Function(Nil, untpd.TypedSplice(res))) - } - else ctx.error(msg, tree.pos) - } - res - } - - /** Retrieve symbol attached to given tree */ - protected def retrieveSym(tree: untpd.Tree)(implicit ctx: Context) = tree.removeAttachment(SymOfTree) match { - case Some(sym) => - sym.ensureCompleted() - sym - case none => - NoSymbol - } - - /** A fresh local context with given tree and owner. - * Owner might not exist (can happen for self valdefs), in which case - * no owner is set in result context - */ - protected def localContext(tree: untpd.Tree, owner: Symbol)(implicit ctx: Context): FreshContext = { - val freshCtx = ctx.fresh.setTree(tree) - if (owner.exists) freshCtx.setOwner(owner) else freshCtx - } - - protected def localTyper(sym: Symbol): Typer = nestedTyper.remove(sym).get - - def typedUnadapted(initTree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = { - record("typedUnadapted") - val xtree = expanded(initTree) - xtree.removeAttachment(TypedAhead) match { - case Some(ttree) => ttree - case none => - - def typedNamed(tree: untpd.NameTree, pt: Type)(implicit ctx: Context): Tree = { - val sym = retrieveSym(xtree) - tree match { - case tree: untpd.Ident => typedIdent(tree, pt) - case tree: untpd.Select => typedSelect(tree, pt) - case tree: untpd.Bind => typedBind(tree, pt) - case tree: untpd.ValDef => - if (tree.isEmpty) tpd.EmptyValDef - else typedValDef(tree, sym)(localContext(tree, sym).setNewScope) - case tree: untpd.DefDef => - val typer1 = localTyper(sym) - typer1.typedDefDef(tree, sym)(localContext(tree, sym).setTyper(typer1)) - case tree: untpd.TypeDef => - if (tree.isClassDef) - typedClassDef(tree, sym.asClass)(localContext(tree, sym).setMode(ctx.mode &~ Mode.InSuperCall)) - else - typedTypeDef(tree, sym)(localContext(tree, sym).setNewScope) - case _ => typedUnadapted(desugar(tree), pt) - } - } - - def typedUnnamed(tree: untpd.Tree): Tree = tree match { - case tree: untpd.Apply => - if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) else typedApply(tree, pt) - case tree: untpd.This => typedThis(tree) - case tree: untpd.Literal => typedLiteral(tree) - case tree: untpd.New => typedNew(tree, pt) - case tree: untpd.Typed => typedTyped(tree, pt) - case tree: untpd.NamedArg => typedNamedArg(tree, pt) - case tree: untpd.Assign => typedAssign(tree, pt) - case tree: untpd.Block => typedBlock(desugar.block(tree), pt)(ctx.fresh.setNewScope) - case tree: untpd.If => typedIf(tree, pt) - case tree: untpd.Function => typedFunction(tree, pt) - case tree: untpd.Closure => typedClosure(tree, pt) - case tree: untpd.Match => typedMatch(tree, pt) - case tree: untpd.Return => typedReturn(tree) - case tree: untpd.Try => typedTry(tree, pt) - case tree: untpd.Throw => typedThrow(tree) - case tree: untpd.TypeApply => typedTypeApply(tree, pt) - case tree: untpd.Super => typedSuper(tree, pt) - case tree: untpd.SeqLiteral => typedSeqLiteral(tree, pt) - case tree: untpd.Inlined => typedInlined(tree, pt) - case tree: untpd.TypeTree => typedTypeTree(tree, pt) - case tree: untpd.SingletonTypeTree => typedSingletonTypeTree(tree) - case tree: untpd.AndTypeTree => typedAndTypeTree(tree) - case tree: untpd.OrTypeTree => typedOrTypeTree(tree) - case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree) - case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree) - case tree: untpd.PolyTypeTree => typedPolyTypeTree(tree)(localContext(tree, NoSymbol).setNewScope) - case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree) - case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree) - case tree: untpd.Alternative => typedAlternative(tree, pt) - case tree: untpd.PackageDef => typedPackageDef(tree) - case tree: untpd.Annotated => typedAnnotated(tree, pt) - case tree: untpd.TypedSplice => typedTypedSplice(tree) - case tree: untpd.UnApply => typedUnApply(tree, pt) - case tree @ untpd.PostfixOp(qual, nme.WILDCARD) => typedAsFunction(tree, pt) - case untpd.EmptyTree => tpd.EmptyTree - case _ => typedUnadapted(desugar(tree), pt) - } - - xtree match { - case xtree: untpd.NameTree => typedNamed(encodeName(xtree), pt) - case xtree: untpd.Import => typedImport(xtree, retrieveSym(xtree)) - case xtree => typedUnnamed(xtree) - } - } - } - - protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree = - untpd.rename(tree, tree.name.encode) - - def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ { - assertPositioned(tree) - try adapt(typedUnadapted(tree, pt), pt, tree) - catch { - case ex: CyclicReference => errorTree(tree, cyclicErrorMsg(ex)) - case ex: TypeError => errorTree(tree, ex.getMessage) - } - } - - def typedTrees(trees: List[untpd.Tree])(implicit ctx: Context): List[Tree] = - trees mapconserve (typed(_)) - - def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[tpd.Tree] = { - val buf = new mutable.ListBuffer[Tree] - @tailrec def traverse(stats: List[untpd.Tree])(implicit ctx: Context): List[Tree] = stats match { - case (imp: untpd.Import) :: rest => - val imp1 = typed(imp) - buf += imp1 - traverse(rest)(importContext(imp1.symbol, imp.selectors)) - case (mdef: untpd.DefTree) :: rest => - mdef.removeAttachment(ExpandedTree) match { - case Some(xtree) => - traverse(xtree :: rest) - case none => - typed(mdef) match { - case mdef1: DefDef if Inliner.hasBodyToInline(mdef1.symbol) => - buf ++= inlineExpansion(mdef1) - case mdef1 => - buf += mdef1 - } - traverse(rest) - } - case Thicket(stats) :: rest => - traverse(stats ++ rest) - case stat :: rest => - buf += typed(stat)(ctx.exprContext(stat, exprOwner)) - traverse(rest) - case nil => - buf.toList - } - traverse(stats) - } - - /** Given an inline method `mdef`, the method rewritten so that its body - * uses accessors to access non-public members, followed by the accessor definitions. - * Overwritten in Retyper to return `mdef` unchanged. - */ - protected def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] = - tpd.cpy.DefDef(mdef)(rhs = Inliner.bodyToInline(mdef.symbol)) :: - Inliner.removeInlineAccessors(mdef.symbol) - - def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = - typed(tree, pt)(ctx retractMode Mode.PatternOrType) - def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = // todo: retract mode between Type and Pattern? - typed(tree, pt)(ctx addMode Mode.Type) - def typedPattern(tree: untpd.Tree, selType: Type = WildcardType)(implicit ctx: Context): Tree = - typed(tree, selType)(ctx addMode Mode.Pattern) - - def tryEither[T](op: Context => T)(fallBack: (T, TyperState) => T)(implicit ctx: Context) = { - val nestedCtx = ctx.fresh.setNewTyperState - val result = op(nestedCtx) - if (nestedCtx.reporter.hasErrors) - fallBack(result, nestedCtx.typerState) - else { - nestedCtx.typerState.commit() - result - } - } - - /** Try `op1`, if there are errors, try `op2`, if `op2` also causes errors, fall back - * to errors and result of `op1`. - */ - def tryAlternatively[T](op1: Context => T)(op2: Context => T)(implicit ctx: Context): T = - tryEither(op1) { (failedVal, failedState) => - tryEither(op2) { (_, _) => - failedState.commit - failedVal - } - } - - /** Add apply node or implicit conversions. Two strategies are tried, and the first - * that is successful is picked. If neither of the strategies are successful, continues with - * `fallBack`. - * - * 1st strategy: Try to insert `.apply` so that the result conforms to prototype `pt`. - * 2nd strategy: If tree is a select `qual.name`, try to insert an implicit conversion - * around the qualifier part `qual` so that the result conforms to the expected type - * with wildcard result type. - */ - def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree = - tryEither { implicit ctx => - val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) - if (sel.tpe.isError) sel else adapt(sel, pt) - } { (failedTree, failedState) => - tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack(failedTree, failedState)) - } - - /** If this tree is a select node `qual.name`, try to insert an implicit conversion - * `c` around `qual` so that `c(qual).name` conforms to `pt`. - */ - def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Option[Tree] = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") { - tree match { - case Select(qual, name) => - val qualProto = SelectionProto(name, pt, NoViewsAllowed) - tryEither { implicit ctx => - val qual1 = adaptInterpolated(qual, qualProto, EmptyTree) - if ((qual eq qual1) || ctx.reporter.hasErrors) None - else Some(typed(cpy.Select(tree)(untpd.TypedSplice(qual1), name), pt)) - } { (_, _) => None - } - case _ => None - } - } - - def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context): Tree = /*>|>*/ track("adapt") /*<|<*/ { - /*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ { - if (tree.isDef) interpolateUndetVars(tree, tree.symbol) - else if (!tree.tpe.widen.isInstanceOf[MethodOrPoly]) interpolateUndetVars(tree, NoSymbol) - tree.overwriteType(tree.tpe.simplified) - adaptInterpolated(tree, pt, original) - } - } - - /** (-1) For expressions with annotated types, let AnnotationCheckers decide what to do - * (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode) - */ - - /** Perform the following adaptations of expression, pattern or type `tree` wrt to - * given prototype `pt`: - * (1) Resolve overloading - * (2) Apply parameterless functions - * (3) Apply polymorphic types to fresh instances of their type parameters and - * store these instances in context.undetparams, - * unless followed by explicit type application. - * (4) Do the following to unapplied methods used as values: - * (4.1) If the method has only implicit parameters pass implicit arguments - * (4.2) otherwise, if `pt` is a function type and method is not a constructor, - * convert to function by eta-expansion, - * (4.3) otherwise, if the method is nullary with a result type compatible to `pt` - * and it is not a constructor, apply it to () - * otherwise issue an error - * (5) Convert constructors in a pattern as follows: - * (5.1) If constructor refers to a case class factory, set tree's type to the unique - * instance of its primary constructor that is a subtype of the expected type. - * (5.2) If constructor refers to an extractor, convert to application of - * unapply or unapplySeq method. - * - * (6) Convert all other types to TypeTree nodes. - * (7) When in TYPEmode but not FUNmode or HKmode, check that types are fully parameterized - * (7.1) In HKmode, higher-kinded types are allowed, but they must have the expected kind-arity - * (8) When in both EXPRmode and FUNmode, add apply method calls to values of object type. - * (9) If there are undetermined type variables and not POLYmode, infer expression instance - * Then, if tree's type is not a subtype of expected type, try the following adaptations: - * (10) If the expected type is Byte, Short or Char, and the expression - * is an integer fitting in the range of that type, convert it to that type. - * (11) Widen numeric literals to their expected type, if necessary - * (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit. - * (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated. - * (14) When in mode EXPRmode, apply a view - * If all this fails, error - */ - def adaptInterpolated(tree: Tree, pt: Type, original: untpd.Tree)(implicit ctx: Context): Tree = { - - assert(pt.exists) - - def methodStr = err.refStr(methPart(tree).tpe) - - def missingArgs = errorTree(tree, - em"""missing arguments for $methodStr - |follow this method with `_' if you want to treat it as a partially applied function""") - - def adaptOverloaded(ref: TermRef) = { - val altDenots = ref.denot.alternatives - typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%, %") - val alts = altDenots map (alt => - TermRef.withSigAndDenot(ref.prefix, ref.name, alt.info.signature, alt)) - def expectedStr = err.expectedTypeStr(pt) - resolveOverloaded(alts, pt) match { - case alt :: Nil => - adapt(tree.withType(alt), pt, original) - case Nil => - def noMatches = - errorTree(tree, - em"""none of the ${err.overloadedAltsStr(altDenots)} - |match $expectedStr""") - def hasEmptyParams(denot: SingleDenotation) = denot.info.paramTypess == ListOfNil - pt match { - case pt: FunProto => - tryInsertApplyOrImplicit(tree, pt)((_, _) => noMatches) - case _ => - if (altDenots exists (_.info.paramTypess == ListOfNil)) - typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt) - else - noMatches - } - case alts => - val remainingDenots = alts map (_.denot.asInstanceOf[SingleDenotation]) - def all = if (remainingDenots.length == 2) "both" else "all" - errorTree(tree, - em"""Ambiguous overload. The ${err.overloadedAltsStr(remainingDenots)} - |$all match $expectedStr""") - } - } - - def isUnary(tp: Type): Boolean = tp match { - case tp: MethodicType => - tp.firstParamTypes match { - case ptype :: Nil => !ptype.isRepeatedParam - case _ => false - } - case tp: TermRef => - tp.denot.alternatives.forall(alt => isUnary(alt.info)) - case _ => - false - } - - def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match { - case _: MethodType | _: PolyType => - if (pt.args.lengthCompare(1) > 0 && isUnary(wtp) && ctx.canAutoTuple) - adaptInterpolated(tree, pt.tupled, original) - else - tree - case _ => tryInsertApplyOrImplicit(tree, pt) { - val more = tree match { - case Apply(_, _) => " more" - case _ => "" - } - (_, _) => errorTree(tree, em"$methodStr does not take$more parameters") - } - } - - /** If `tp` is a TypeVar which is fully constrained (i.e. its upper bound `hi` conforms - * to its lower bound `lo`), replace `tp` by `hi`. This is necessary to - * keep the right constraints for some implicit search problems. The paradigmatic case - * is `implicitNums.scala`. Without the healing done in `followAlias`, we cannot infer - * implicitly[_3], where _2 is the typelevel number 3. The problem here is that if a - * prototype is, say, Succ[Succ[Zero]], we can infer that it's argument type is Succ[Zero]. - * But if the prototype is N? >: Succ[Succ[Zero]] <: Succ[Succ[Zero]], the same - * decomposition does not work - we'd get a N?#M where M is the element type name of Succ - * instead. - */ - def followAlias(tp: Type)(implicit ctx: Context): Type = { - val constraint = ctx.typerState.constraint - def inst(tp: Type): Type = tp match { - case TypeBounds(lo, hi) - if (lo eq hi) || (hi <:< lo)(ctx.fresh.setExploreTyperState) => - inst(lo) - case tp: PolyParam => - constraint.typeVarOfParam(tp).orElse(tp) - case _ => tp - } - tp match { - case tp: TypeVar if constraint.contains(tp) => inst(constraint.entry(tp.origin)) - case _ => tp - } - } - - def adaptNoArgs(wtp: Type): Tree = wtp match { - case wtp: ExprType => - adaptInterpolated(tree.withType(wtp.resultType), pt, original) - case wtp: ImplicitMethodType if constrainResult(wtp, followAlias(pt)) => - val tvarsToInstantiate = tvarsInParams(tree) - wtp.paramTypes.foreach(instantiateSelected(_, tvarsToInstantiate)) - val constr = ctx.typerState.constraint - def addImplicitArgs(implicit ctx: Context) = { - val errors = new mutable.ListBuffer[() => String] - def implicitArgError(msg: => String) = { - errors += (() => msg) - EmptyTree - } - def issueErrors() = { - for (err <- errors) ctx.error(err(), tree.pos.endPos) - tree.withType(wtp.resultType) - } - val args = (wtp.paramNames, wtp.paramTypes).zipped map { (pname, formal) => - def implicitArgError(msg: String => String) = - errors += (() => msg(em"parameter $pname of $methodStr")) - inferImplicitArg(formal, implicitArgError, tree.pos.endPos) - } - if (errors.nonEmpty) { - // If there are several arguments, some arguments might already - // have influenced the context, binding variables, but later ones - // might fail. In that case the constraint needs to be reset. - ctx.typerState.constraint = constr - - // If method has default params, fall back to regular application - // where all inferred implicits are passed as named args. - if (tree.symbol.hasDefaultParams) { - val namedArgs = (wtp.paramNames, args).zipped.flatMap { (pname, arg) => - arg match { - case EmptyTree => Nil - case _ => untpd.NamedArg(pname, untpd.TypedSplice(arg)) :: Nil - } - } - tryEither { implicit ctx => - typed(untpd.Apply(untpd.TypedSplice(tree), namedArgs), pt) - } { (_, _) => - issueErrors() - } - } else issueErrors() - } - else adapt(tpd.Apply(tree, args), pt) - } - if ((pt eq WildcardType) || original.isEmpty) addImplicitArgs(argCtx(tree)) - else - ctx.typerState.tryWithFallback(addImplicitArgs(argCtx(tree))) { - adapt(typed(original, WildcardType), pt, EmptyTree) - } - case wtp: MethodType if !pt.isInstanceOf[SingletonType] => - val arity = - if (defn.isFunctionType(pt)) - if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none)) - // if method type is fully defined, but expected type is not, - // prioritize method parameter types as parameter types of the eta-expanded closure - 0 - else defn.functionArity(pt) - else if (pt eq AnyFunctionProto) wtp.paramTypes.length - else -1 - if (arity >= 0 && !tree.symbol.isConstructor) - typed(etaExpand(tree, wtp, arity), pt) - else if (wtp.paramTypes.isEmpty) - adaptInterpolated(tpd.Apply(tree, Nil), pt, EmptyTree) - else if (wtp.isImplicit) - err.typeMismatch(tree, pt) - else - missingArgs - case _ => - ctx.typeComparer.GADTused = false - if (ctx.mode is Mode.Pattern) { - tree match { - case _: RefTree | _: Literal if !isVarPattern(tree) => - checkCanEqual(pt, wtp, tree.pos)(ctx.retractMode(Mode.Pattern)) - case _ => - } - tree - } - else if (tree.tpe <:< pt) { - if (pt.hasAnnotation(defn.InlineParamAnnot)) - checkInlineConformant(tree, "argument to inline parameter") - if (Inliner.hasBodyToInline(tree.symbol) && - !ctx.owner.ownersIterator.exists(_.isInlineMethod) && - !ctx.settings.YnoInline.value && - !ctx.isAfterTyper) - adapt(Inliner.inlineCall(tree, pt), pt) - else if (ctx.typeComparer.GADTused && pt.isValueType) - // Insert an explicit cast, so that -Ycheck in later phases succeeds. - // I suspect, but am not 100% sure that this might affect inferred types, - // if the expected type is a supertype of the GADT bound. It would be good to come - // up with a test case for this. - tree.asInstance(pt) - else - tree - } - else if (wtp.isInstanceOf[MethodType]) missingArgs - else { - typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt") - //typr.println(TypeComparer.explained(implicit ctx => tree.tpe <:< pt)) - adaptToSubType(wtp) - } - } - /** Adapt an expression of constant type to a different constant type `tpe`. */ - def adaptConstant(tree: Tree, tpe: ConstantType): Tree = { - def lit = Literal(tpe.value).withPos(tree.pos) - tree match { - case Literal(c) => lit - case tree @ Block(stats, expr) => tpd.cpy.Block(tree)(stats, adaptConstant(expr, tpe)) - case tree => - if (isIdempotentExpr(tree)) lit // See discussion in phase Literalize why we demand isIdempotentExpr - else Block(tree :: Nil, lit) - } - } - - def adaptToSubType(wtp: Type): Tree = { - // try converting a constant to the target type - val folded = ConstFold(tree, pt) - if (folded ne tree) return adaptConstant(folded, folded.tpe.asInstanceOf[ConstantType]) - // drop type if prototype is Unit - if (pt isRef defn.UnitClass) - // local adaptation makes sure every adapted tree conforms to its pt - // so will take the code path that decides on inlining - return tpd.Block(adapt(tree, WildcardType) :: Nil, Literal(Constant(()))) - // convert function literal to SAM closure - tree match { - case Closure(Nil, id @ Ident(nme.ANON_FUN), _) - if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) => - pt match { - case SAMType(meth) - if wtp <:< meth.info.toFunctionType() => - // was ... && isFullyDefined(pt, ForceDegree.noBottom) - // but this prevents case blocks from implementing polymorphic partial functions, - // since we do not know the result parameter a priori. Have to wait until the - // body is typechecked. - return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt) - case _ => - } - case _ => - } - // try an implicit conversion - inferView(tree, pt) match { - case SearchSuccess(inferred, _, _) => - adapt(inferred, pt) - case failure: SearchFailure => - if (pt.isInstanceOf[ProtoType] && !failure.isInstanceOf[AmbiguousImplicits]) tree - else err.typeMismatch(tree, pt, failure) - } - } - - def adaptType(tp: Type): Tree = { - val tree1 = - if ((pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty) tree - else tree.withType(tree.tpe.EtaExpand(tp.typeParamSymbols)) - if ((ctx.mode is Mode.Pattern) || tree1.tpe <:< pt) tree1 - else err.typeMismatch(tree1, pt) - } - - tree match { - case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[_] => tree - case _ => tree.tpe.widen match { - case _: ErrorType => - tree - case ref: TermRef => - pt match { - case pt: FunProto - if pt.args.lengthCompare(1) > 0 && isUnary(ref) && ctx.canAutoTuple => - adaptInterpolated(tree, pt.tupled, original) - case _ => - adaptOverloaded(ref) - } - case poly: PolyType if !(ctx.mode is Mode.Type) => - if (pt.isInstanceOf[PolyProto]) tree - else { - var typeArgs = tree match { - case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo - case _ => Nil - } - if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2 - convertNewGenericArray( - adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original)) - } - case wtp => - pt match { - case pt: FunProto => - adaptToArgs(wtp, pt) - case pt: PolyProto => - tryInsertApplyOrImplicit(tree, pt) { - (_, _) => tree // error will be reported in typedTypeApply - } - case _ => - if (ctx.mode is Mode.Type) adaptType(tree.tpe) - else adaptNoArgs(wtp) - } - } - } - } -} diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala deleted file mode 100644 index d5dd5a024..000000000 --- a/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ /dev/null @@ -1,148 +0,0 @@ -package dotty.tools.dotc -package typer - -import dotty.tools.dotc.ast.{ Trees, tpd } -import core._ -import Types._, Contexts._, Flags._, Symbols._, Annotations._, Trees._, NameOps._ -import Decorators._ -import Variances._ -import util.Positions._ -import rewrite.Rewrites.patch -import config.Printers.variances - -/** Provides `check` method to check that all top-level definitions - * in tree are variance correct. Does not recurse inside methods. - * The method should be invoked once for each Template. - */ -object VarianceChecker { - case class VarianceError(tvar: Symbol, required: Variance) - def check(tree: tpd.Tree)(implicit ctx: Context) = - new VarianceChecker()(ctx).Traverser.traverse(tree) -} - -class VarianceChecker()(implicit ctx: Context) { - import VarianceChecker._ - import tpd._ - - private object Validator extends TypeAccumulator[Option[VarianceError]] { - private var base: Symbol = _ - - /** Is no variance checking needed within definition of `base`? */ - def ignoreVarianceIn(base: Symbol): Boolean = ( - base.isTerm - || base.is(Package) - || base.is(Local) - ) - - /** The variance of a symbol occurrence of `tvar` seen at the level of the definition of `base`. - * The search proceeds from `base` to the owner of `tvar`. - * Initially the state is covariant, but it might change along the search. - */ - def relativeVariance(tvar: Symbol, base: Symbol, v: Variance = Covariant): Variance = /*ctx.traceIndented(i"relative variance of $tvar wrt $base, so far: $v")*/ { - if (base == tvar.owner) v - else if ((base is Param) && base.owner.isTerm) - relativeVariance(tvar, paramOuter(base.owner), flip(v)) - else if (ignoreVarianceIn(base.owner)) Bivariant - else if (base.isAliasType) relativeVariance(tvar, base.owner, Invariant) - else relativeVariance(tvar, base.owner, v) - } - - /** The next level to take into account when determining the - * relative variance with a method parameter as base. The method - * is always skipped. If the method is a constructor, we also skip - * its class owner, because constructors are not checked for variance - * relative to the type parameters of their own class. On the other - * hand constructors do count for checking the variance of type parameters - * of enclosing classes. I believe the Scala 2 rules are too lenient in - * that respect. - */ - private def paramOuter(meth: Symbol) = - if (meth.isConstructor) meth.owner.owner else meth.owner - - /** Check variance of abstract type `tvar` when referred from `base`. */ - private def checkVarianceOfSymbol(tvar: Symbol): Option[VarianceError] = { - val relative = relativeVariance(tvar, base) - if (relative == Bivariant || tvar.is(BaseTypeArg)) None - else { - val required = compose(relative, this.variance) - def tvar_s = s"$tvar (${varianceString(tvar.flags)} ${tvar.showLocated})" - def base_s = s"$base in ${base.owner}" + (if (base.owner.isClass) "" else " in " + base.owner.enclosingClass) - ctx.log(s"verifying $tvar_s is ${varianceString(required)} at $base_s") - ctx.log(s"relative variance: ${varianceString(relative)}") - ctx.log(s"current variance: ${this.variance}") - ctx.log(s"owner chain: ${base.ownersIterator.toList}") - if (tvar is required) None - else Some(VarianceError(tvar, required)) - } - } - - /** For PolyTypes, type parameters are skipped because they are defined - * explicitly (their TypeDefs will be passed here.) For MethodTypes, the - * same is true of the parameters (ValDefs). - */ - def apply(status: Option[VarianceError], tp: Type): Option[VarianceError] = ctx.traceIndented(s"variance checking $tp of $base at $variance", variances) { - if (status.isDefined) status - else tp match { - case tp: TypeRef => - val sym = tp.symbol - if (sym.variance != 0 && base.isContainedIn(sym.owner)) checkVarianceOfSymbol(sym) - else if (sym.isAliasType) this(status, sym.info.bounds.hi) - else foldOver(status, tp) - case tp: MethodType => - this(status, tp.resultType) // params will be checked in their TypeDef nodes. - case tp: PolyType => - this(status, tp.resultType) // params will be checked in their ValDef nodes. - case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedVarianceAnnot => - status - //case tp: ClassInfo => - // ??? not clear what to do here yet. presumably, it's all checked at local typedefs - case _ => - foldOver(status, tp) - } - } - - def validateDefinition(base: Symbol): Option[VarianceError] = { - val saved = this.base - this.base = base - try apply(None, base.info) - finally this.base = saved - } - } - - private object Traverser extends TreeTraverser { - def checkVariance(sym: Symbol, pos: Position) = Validator.validateDefinition(sym) match { - case Some(VarianceError(tvar, required)) => - def msg = i"${varianceString(tvar.flags)} $tvar occurs in ${varianceString(required)} position in type ${sym.info} of $sym" - if (ctx.scala2Mode && sym.owner.isConstructor) { - ctx.migrationWarning(s"According to new variance rules, this is no longer accepted; need to annotate with @uncheckedVariance:\n$msg", sym.pos) - patch(Position(pos.end), " @scala.annotation.unchecked.uncheckedVariance") // TODO use an import or shorten if possible - } - else ctx.error(msg, sym.pos) - case None => - } - - override def traverse(tree: Tree)(implicit ctx: Context) = { - def sym = tree.symbol - // No variance check for private/protected[this] methods/values. - def skip = - !sym.exists || - sym.is(Local) || // !!! watch out for protected local! - sym.is(TypeParam) && sym.owner.isClass // already taken care of in primary constructor of class - tree match { - case defn: MemberDef if skip => - ctx.debuglog(s"Skipping variance check of ${sym.showDcl}") - case tree: TypeDef => - checkVariance(sym, tree.pos) - case tree: ValDef => - checkVariance(sym, tree.pos) - case DefDef(_, tparams, vparamss, _, _) => - checkVariance(sym, tree.pos) - tparams foreach traverse - vparamss foreach (_ foreach traverse) - case Template(_, _, _, body) => - traverseChildren(tree) - case _ => - } - } - } -} diff --git a/src/dotty/tools/dotc/typer/Variances.scala b/src/dotty/tools/dotc/typer/Variances.scala deleted file mode 100644 index 92bd9fd74..000000000 --- a/src/dotty/tools/dotc/typer/Variances.scala +++ /dev/null @@ -1,116 +0,0 @@ -package dotty.tools.dotc -package typer - -import dotty.tools.dotc.ast.{Trees, tpd} -import core._ -import Types._, Contexts._, Flags._, Symbols._, Annotations._, Trees._ -import Decorators._ - -object Variances { - import tpd._ - - type Variance = FlagSet - val Bivariant = VarianceFlags - val Invariant = EmptyFlags - - /** Flip between covariant and contravariant */ - def flip(v: Variance): Variance = { - if (v == Covariant) Contravariant - else if (v == Contravariant) Covariant - else v - } - - /** Map everything below Bivariant to Invariant */ - def cut(v: Variance): Variance = - if (v == Bivariant) v else Invariant - - def compose(v: Variance, boundsVariance: Int) = - if (boundsVariance == 1) v - else if (boundsVariance == -1) flip(v) - else cut(v) - - /** Compute variance of type parameter `tparam' in types of all symbols `sym'. */ - def varianceInSyms(syms: List[Symbol])(tparam: Symbol)(implicit ctx: Context): Variance = - (Bivariant /: syms) ((v, sym) => v & varianceInSym(sym)(tparam)) - - /** Compute variance of type parameter `tparam' in type of symbol `sym'. */ - def varianceInSym(sym: Symbol)(tparam: Symbol)(implicit ctx: Context): Variance = - if (sym.isAliasType) cut(varianceInType(sym.info)(tparam)) - else varianceInType(sym.info)(tparam) - - /** Compute variance of type parameter `tparam' in all types `tps'. */ - def varianceInTypes(tps: List[Type])(tparam: Symbol)(implicit ctx: Context): Variance = - (Bivariant /: tps) ((v, tp) => v & varianceInType(tp)(tparam)) - - /** Compute variance of type parameter `tparam' in all type arguments - * <code>tps</code> which correspond to formal type parameters `tparams1'. - */ - def varianceInArgs(tps: List[Type], tparams1: List[Symbol])(tparam: Symbol)(implicit ctx: Context): Variance = { - var v: Variance = Bivariant; - for ((tp, tparam1) <- tps zip tparams1) { - val v1 = varianceInType(tp)(tparam) - v = v & (if (tparam1.is(Covariant)) v1 - else if (tparam1.is(Contravariant)) flip(v1) - else cut(v1)) - } - v - } - - /** Compute variance of type parameter `tparam' in all type annotations `annots'. */ - def varianceInAnnots(annots: List[Annotation])(tparam: Symbol)(implicit ctx: Context): Variance = { - (Bivariant /: annots) ((v, annot) => v & varianceInAnnot(annot)(tparam)) - } - - /** Compute variance of type parameter `tparam' in type annotation `annot'. */ - def varianceInAnnot(annot: Annotation)(tparam: Symbol)(implicit ctx: Context): Variance = { - varianceInType(annot.tree.tpe)(tparam) - } - - /** Compute variance of type parameter <code>tparam</code> in type <code>tp</code>. */ - def varianceInType(tp: Type)(tparam: Symbol)(implicit ctx: Context): Variance = tp match { - case TermRef(pre, _) => - varianceInType(pre)(tparam) - case tp @ TypeRef(pre, _) => - if (tp.symbol == tparam) Covariant else varianceInType(pre)(tparam) - case tp @ TypeBounds(lo, hi) => - if (lo eq hi) compose(varianceInType(hi)(tparam), tp.variance) - else flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam) - case tp @ RefinedType(parent, _, rinfo) => - varianceInType(parent)(tparam) & varianceInType(rinfo)(tparam) - case tp: RecType => - varianceInType(tp.parent)(tparam) - case tp @ MethodType(_, paramTypes) => - flip(varianceInTypes(paramTypes)(tparam)) & varianceInType(tp.resultType)(tparam) - case ExprType(restpe) => - varianceInType(restpe)(tparam) - case tp @ HKApply(tycon, args) => - def varianceInArgs(v: Variance, args: List[Type], tparams: List[TypeParamInfo]): Variance = - args match { - case arg :: args1 => - varianceInArgs( - v & compose(varianceInType(arg)(tparam), tparams.head.paramVariance), - args1, tparams.tail) - case nil => - v - } - varianceInArgs(varianceInType(tycon)(tparam), args, tycon.typeParams) - case tp: PolyType => - flip(varianceInTypes(tp.paramBounds)(tparam)) & varianceInType(tp.resultType)(tparam) - case AnnotatedType(tp, annot) => - varianceInType(tp)(tparam) & varianceInAnnot(annot)(tparam) - case tp: AndOrType => - varianceInType(tp.tp1)(tparam) & varianceInType(tp.tp2)(tparam) - case _ => - Bivariant - } - - def varianceString(v: Variance) = - if (v is Covariant) "covariant" - else if (v is Contravariant) "contravariant" - else "invariant" - - def varianceString(v: Int) = - if (v > 0) "+" - else if (v < 0) "-" - else "" -} diff --git a/src/dotty/tools/dotc/util/Attachment.scala b/src/dotty/tools/dotc/util/Attachment.scala deleted file mode 100644 index 20facfd97..000000000 --- a/src/dotty/tools/dotc/util/Attachment.scala +++ /dev/null @@ -1,96 +0,0 @@ -package dotty.tools.dotc.util - -/** A class inheriting from Attachment.Container supports - * adding, removing and lookup of attachments. Attachments are typed key/value pairs. - */ -object Attachment { - import Property.Key - - /** An implementation trait for attachments. - * Clients should inherit from Container instead. - */ - trait LinkSource { - private[Attachment] var next: Link[_] - - /** Optionally get attachment corresponding to `key` */ - final def getAttachment[V](key: Key[V]): Option[V] = { - val nx = next - if (nx == null) None - else if (nx.key eq key) Some(nx.value.asInstanceOf[V]) - else nx.getAttachment[V](key) - } - - /** The attachment corresponding to `key`. - * @throws NoSuchElementException if no attachment with key exists - */ - final def attachment[V](key: Key[V]): V = { - val nx = next - if (nx == null) throw new NoSuchElementException - else if (nx.key eq key) nx.value.asInstanceOf[V] - else nx.attachment(key) - } - - /** The attachment corresponding to `key`, or `default` - * if no attachment with `key` exists. - */ - final def attachmentOrElse[V](key: Key[V], default: V): V = { - val nx = next - if (nx == null) default - else if (nx.key eq key) nx.value.asInstanceOf[V] - else nx.attachmentOrElse(key, default) - } - - /** Add attachment with given `key` and `value`. - * @return Optionally, the old attachment with given `key` if one existed before. - * The new attachment is added at the position of the old one, or at the end - * if no attachment with same `key` existed. - */ - final def putAttachment[V](key: Key[V], value: V): Option[V] = { - val nx = next - if (nx == null) { - next = new Link(key, value, null) - None - } - else if (nx.key eq key) { - next = new Link(key, value, nx.next) - Some(nx.value.asInstanceOf[V]) - } - else nx.putAttachment(key, value) - } - - /** Remove attachment with given `key`, if it exists. - * @return Optionally, the removed attachment with given `key` if one existed before. - */ - final def removeAttachment[V](key: Key[V]): Option[V] = { - val nx = next - if (nx == null) - None - else if (nx.key eq key) { - next = nx.next - Some(nx.value.asInstanceOf[V]) - } - else nx.removeAttachment(key) - } - - /** The list of all keys and values attached to this container. */ - final def allAttachments: List[(Key[_], Any)] = { - val nx = next - if (nx == null) Nil else (nx.key, nx.value) :: nx.allAttachments - } - } - - /** A private, concrete implementation class linking attachments. - */ - private[Attachment] class Link[+V](val key: Key[V], val value: V, var next: Link[_]) - extends LinkSource - - /** A trait for objects that can contain attachments */ - trait Container extends LinkSource { - private[Attachment] var next: Link[_] = null - - final def pushAttachment[V](key: Key[V], value: V): Unit = { - assert(!getAttachment(key).isDefined, s"duplicate attachment for key $key") - next = new Link(key, value, next) - } - } -} diff --git a/src/dotty/tools/dotc/util/Chars.scala b/src/dotty/tools/dotc/util/Chars.scala deleted file mode 100644 index bae3b4732..000000000 --- a/src/dotty/tools/dotc/util/Chars.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2006-2012 LAMP/EPFL - * @author Martin Odersky - */ -package dotty.tools.dotc -package util - -import scala.annotation.switch -import java.lang.{ Character => JCharacter } -import java.lang.{Character => JCharacter} -import java.lang.Character.LETTER_NUMBER -import java.lang.Character.LOWERCASE_LETTER -import java.lang.Character.OTHER_LETTER -import java.lang.Character.TITLECASE_LETTER -import java.lang.Character.UPPERCASE_LETTER - -/** Contains constants and classifier methods for characters */ -object Chars { - - final val LF = '\u000A' - final val FF = '\u000C' - final val CR = '\u000D' - final val SU = '\u001A' - - /** Convert a character digit to an Int according to given base, - * -1 if no success - */ - def digit2int(ch: Char, base: Int): Int = { - val num = ( - if (ch <= '9') ch - '0' - else if ('a' <= ch && ch <= 'z') ch - 'a' + 10 - else if ('A' <= ch && ch <= 'Z') ch - 'A' + 10 - else -1 - ) - if (0 <= num && num < base) num else -1 - } - /** Buffer for creating '\ u XXXX' strings. */ - private[this] val char2uescapeArray = Array[Char]('\\', 'u', 0, 0, 0, 0) - - /** Convert a character to a backslash-u escape */ - def char2uescape(c: Char): String = { - @inline def hexChar(ch: Int): Char = - (( if (ch < 10) '0' else 'A' - 10 ) + ch).toChar - - char2uescapeArray(2) = hexChar((c >> 12) ) - char2uescapeArray(3) = hexChar((c >> 8) % 16) - char2uescapeArray(4) = hexChar((c >> 4) % 16) - char2uescapeArray(5) = hexChar((c ) % 16) - - new String(char2uescapeArray) - } - - /** Is character a line break? */ - def isLineBreakChar(c: Char) = (c: @switch) match { - case LF|FF|CR|SU => true - case _ => false - } - - /** Is character a whitespace character (but not a new line)? */ - def isWhitespace(c: Char) = - c == ' ' || c == '\t' || c == CR - - /** Can character form part of a doc comment variable $xxx? */ - def isVarPart(c: Char) = - '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' - - /** Can character start an alphanumeric Scala identifier? */ - def isIdentifierStart(c: Char): Boolean = - (c == '_') || (c == '$') || Character.isUnicodeIdentifierStart(c) - - /** Can character form part of an alphanumeric Scala identifier? */ - def isIdentifierPart(c: Char) = - (c == '$') || Character.isUnicodeIdentifierPart(c) - - /** Is character a math or other symbol in Unicode? */ - def isSpecial(c: Char) = { - val chtp = Character.getType(c) - chtp == Character.MATH_SYMBOL.toInt || chtp == Character.OTHER_SYMBOL.toInt - } - - private final val otherLetters = Set[Char]('\u0024', '\u005F') // '$' and '_' - private final val letterGroups = { - import JCharacter._ - Set[Byte](LOWERCASE_LETTER, UPPERCASE_LETTER, OTHER_LETTER, TITLECASE_LETTER, LETTER_NUMBER) - } - def isScalaLetter(ch: Char) = letterGroups(JCharacter.getType(ch).toByte) || otherLetters(ch) - - /** Can character form part of a Scala operator name? */ - def isOperatorPart(c : Char) : Boolean = (c: @switch) match { - case '~' | '!' | '@' | '#' | '%' | - '^' | '*' | '+' | '-' | '<' | - '>' | '?' | ':' | '=' | '&' | - '|' | '/' | '\\' => true - case c => isSpecial(c) - } -} diff --git a/src/dotty/tools/dotc/util/CommentParsing.scala b/src/dotty/tools/dotc/util/CommentParsing.scala deleted file mode 100644 index cc790d683..000000000 --- a/src/dotty/tools/dotc/util/CommentParsing.scala +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Port of DocStrings.scala from nsc - * @author Martin Odersky - * @author Felix Mulder - */ -package dotty.tools.dotc.util - -/** The comment parsing in `dotc` is used by both the comment cooking and the - * dottydoc tool. - * - * The comment cooking is used to expand comments with `@inheritdoc` and - * `@define` annotations. The rest of the comment is untouched and later - * handled by dottydoc. - */ -object CommentParsing { - import scala.reflect.internal.Chars._ - - /** Returns index of string `str` following `start` skipping longest - * sequence of whitespace characters characters (but no newlines) - */ - def skipWhitespace(str: String, start: Int): Int = - if (start < str.length && isWhitespace(str charAt start)) skipWhitespace(str, start + 1) - else start - - /** Returns index of string `str` following `start` skipping - * sequence of identifier characters. - */ - def skipIdent(str: String, start: Int): Int = - if (start < str.length && isIdentifierPart(str charAt start)) skipIdent(str, start + 1) - else start - - /** Returns index of string `str` following `start` skipping - * sequence of identifier characters. - */ - def skipTag(str: String, start: Int): Int = - if (start < str.length && (str charAt start) == '@') skipIdent(str, start + 1) - else start - - - /** Returns index of string `str` after `start` skipping longest - * sequence of space and tab characters, possibly also containing - * a single `*` character or the `/``**` sequence. - * @pre start == str.length || str(start) == `\n` - */ - def skipLineLead(str: String, start: Int): Int = - if (start == str.length) start - else { - val idx = skipWhitespace(str, start + 1) - if (idx < str.length && (str charAt idx) == '*') skipWhitespace(str, idx + 1) - else if (idx + 2 < str.length && (str charAt idx) == '/' && (str charAt (idx + 1)) == '*' && (str charAt (idx + 2)) == '*') - skipWhitespace(str, idx + 3) - else idx - } - - /** Skips to next occurrence of `\n` or to the position after the `/``**` sequence following index `start`. - */ - def skipToEol(str: String, start: Int): Int = - if (start + 2 < str.length && (str charAt start) == '/' && (str charAt (start + 1)) == '*' && (str charAt (start + 2)) == '*') start + 3 - else if (start < str.length && (str charAt start) != '\n') skipToEol(str, start + 1) - else start - - /** Returns first index following `start` and starting a line (i.e. after skipLineLead) or starting the comment - * which satisfies predicate `p`. - */ - def findNext(str: String, start: Int)(p: Int => Boolean): Int = { - val idx = skipLineLead(str, skipToEol(str, start)) - if (idx < str.length && !p(idx)) findNext(str, idx)(p) - else idx - } - - /** Return first index following `start` and starting a line (i.e. after skipLineLead) - * which satisfies predicate `p`. - */ - def findAll(str: String, start: Int)(p: Int => Boolean): List[Int] = { - val idx = findNext(str, start)(p) - if (idx == str.length) List() - else idx :: findAll(str, idx)(p) - } - - /** Produces a string index, which is a list of `sections`, i.e - * pairs of start/end positions of all tagged sections in the string. - * Every section starts with an at sign and extends to the next at sign, - * or to the end of the comment string, but excluding the final two - * characters which terminate the comment. - * - * Also take usecases into account - they need to expand until the next - * usecase or the end of the string, as they might include other sections - * of their own - */ - def tagIndex(str: String, p: Int => Boolean = (idx => true)): List[(Int, Int)] = { - var indices = findAll(str, 0) (idx => str(idx) == '@' && p(idx)) - indices = mergeUsecaseSections(str, indices) - indices = mergeInheritdocSections(str, indices) - - indices match { - case List() => List() - case idxs => idxs zip (idxs.tail ::: List(str.length - 2)) - } - } - - /** - * Merge sections following an usecase into the usecase comment, so they - * can override the parent symbol's sections - */ - def mergeUsecaseSections(str: String, idxs: List[Int]): List[Int] = { - idxs.indexWhere(str.startsWith("@usecase", _)) match { - case firstUCIndex if firstUCIndex != -1 => - val commentSections = idxs.take(firstUCIndex) - val usecaseSections = idxs.drop(firstUCIndex).filter(str.startsWith("@usecase", _)) - commentSections ::: usecaseSections - case _ => - idxs - } - } - - /** - * Merge the inheritdoc sections, as they never make sense on their own - */ - def mergeInheritdocSections(str: String, idxs: List[Int]): List[Int] = - idxs.filterNot(str.startsWith("@inheritdoc", _)) - - /** Does interval `iv` start with given `tag`? - */ - def startsWithTag(str: String, section: (Int, Int), tag: String): Boolean = - startsWithTag(str, section._1, tag) - - def startsWithTag(str: String, start: Int, tag: String): Boolean = - str.startsWith(tag, start) && !isIdentifierPart(str charAt (start + tag.length)) - - /** The first start tag of a list of tag intervals, - * or the end of the whole comment string - 2 if list is empty - */ - def startTag(str: String, sections: List[(Int, Int)]) = sections match { - case Nil => str.length - 2 - case (start, _) :: _ => start - } - - /** A map from parameter names to start/end indices describing all parameter - * sections in `str` tagged with `tag`, where `sections` is the index of `str`. - */ - def paramDocs(str: String, tag: String, sections: List[(Int, Int)]): Map[String, (Int, Int)] = - Map() ++ { - for (section <- sections if startsWithTag(str, section, tag)) yield { - val start = skipWhitespace(str, section._1 + tag.length) - str.substring(start, skipIdent(str, start)) -> section - } - } - - /** Optionally start and end index of return section in `str`, or `None` - * if `str` does not have a @group. */ - def groupDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] = - sections find (startsWithTag(str, _, "@group")) - - - /** Optionally start and end index of return section in `str`, or `None` - * if `str` does not have a @return. - */ - def returnDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] = - sections find (startsWithTag(str, _, "@return")) - - /** Extracts variable name from a string, stripping any pair of surrounding braces */ - def variableName(str: String): String = - if (str.length >= 2 && (str charAt 0) == '{' && (str charAt (str.length - 1)) == '}') - str.substring(1, str.length - 1) - else - str - - /** Returns index following variable, or start index if no variable was recognized - */ - def skipVariable(str: String, start: Int): Int = { - var idx = start - if (idx < str.length && (str charAt idx) == '{') { - do idx += 1 - while (idx < str.length && (str charAt idx) != '}') - if (idx < str.length) idx + 1 else start - } else { - while (idx < str.length && isVarPart(str charAt idx)) - idx += 1 - idx - } - } - - /** A map from the section tag to section parameters */ - def sectionTagMap(str: String, sections: List[(Int, Int)]): Map[String, (Int, Int)] = - Map() ++ { - for (section <- sections) yield - extractSectionTag(str, section) -> section - } - - /** Extract the section tag, treating the section tag as an identifier */ - def extractSectionTag(str: String, section: (Int, Int)): String = - str.substring(section._1, skipTag(str, section._1)) - - /** Extract the section parameter */ - def extractSectionParam(str: String, section: (Int, Int)): String = { - val (beg, _) = section - assert(str.startsWith("@param", beg) || - str.startsWith("@tparam", beg) || - str.startsWith("@throws", beg)) - - val start = skipWhitespace(str, skipTag(str, beg)) - val finish = skipIdent(str, start) - - str.substring(start, finish) - } - - /** Extract the section text, except for the tag and comment newlines */ - def extractSectionText(str: String, section: (Int, Int)): (Int, Int) = { - val (beg, end) = section - if (str.startsWith("@param", beg) || - str.startsWith("@tparam", beg) || - str.startsWith("@throws", beg)) - (skipWhitespace(str, skipIdent(str, skipWhitespace(str, skipTag(str, beg)))), end) - else - (skipWhitespace(str, skipTag(str, beg)), end) - } - - /** Cleanup section text */ - def cleanupSectionText(str: String) = { - var result = str.trim.replaceAll("\n\\s+\\*\\s+", " \n") - while (result.endsWith("\n")) - result = result.substring(0, str.length - 1) - result - } - - - def removeSections(raw: String, xs: String*): String = { - val sections = tagIndex(raw) - - val toBeRemoved = for { - section <- xs - lines = sections filter { startsWithTag(raw, _, section) } - } yield lines - - val end = startTag(raw, toBeRemoved.flatten.sortBy(_._1).toList) - - if (end == raw.length - 2) raw else raw.substring(0, end) + "*/" - } -} diff --git a/src/dotty/tools/dotc/util/DiffUtil.scala b/src/dotty/tools/dotc/util/DiffUtil.scala deleted file mode 100644 index b55aee719..000000000 --- a/src/dotty/tools/dotc/util/DiffUtil.scala +++ /dev/null @@ -1,174 +0,0 @@ -package dotty.tools.dotc.util - -import scala.annotation.tailrec -import scala.collection.mutable - -object DiffUtil { - - private final val ANSI_DEFAULT = "\u001B[0m" - private final val ANSI_RED = "\u001B[31m" - private final val ANSI_GREEN = "\u001B[32m" - - private final val DELETION_COLOR = ANSI_RED - private final val ADDITION_COLOR = ANSI_GREEN - - @tailrec private def splitTokens(str: String, acc: List[String] = Nil): List[String] = { - if (str == "") { - acc.reverse - } else { - val head = str.charAt(0) - val (token, rest) = if (Character.isAlphabetic(head) || Character.isDigit(head)) { - str.span(c => Character.isAlphabetic(c) || Character.isDigit(c)) - } else if (Character.isMirrored(head) || Character.isWhitespace(head)) { - str.splitAt(1) - } else { - str.span { c => - !Character.isAlphabetic(c) && !Character.isDigit(c) && - !Character.isMirrored(c) && !Character.isWhitespace(c) - } - } - splitTokens(rest, token :: acc) - } - } - - - /** @return a tuple of the (found, expected, changedPercentage) diffs as strings */ - def mkColoredTypeDiff(found: String, expected: String): (String, String, Double) = { - var totalChange = 0 - val foundTokens = splitTokens(found, Nil).toArray - val expectedTokens = splitTokens(expected, Nil).toArray - - val diffExp = hirschberg(foundTokens, expectedTokens) - val diffAct = hirschberg(expectedTokens, foundTokens) - - val exp = diffExp.collect { - case Unmodified(str) => str - case Inserted(str) => - totalChange += str.length - ADDITION_COLOR + str + ANSI_DEFAULT - }.mkString - - val fnd = diffAct.collect { - case Unmodified(str) => str - case Inserted(str) => - totalChange += str.length - DELETION_COLOR + str + ANSI_DEFAULT - }.mkString - - (fnd, exp, totalChange.toDouble / (expected.length + found.length)) - } - - def mkColoredCodeDiff(code: String, lastCode: String, printDiffDel: Boolean): String = { - - val tokens = splitTokens(code, Nil).toArray - val lastTokens = splitTokens(lastCode, Nil).toArray - - val diff = hirschberg(lastTokens, tokens) - - diff.collect { - case Unmodified(str) => str - case Inserted(str) => ADDITION_COLOR + str + ANSI_DEFAULT - case Modified(old, str) if printDiffDel => DELETION_COLOR + old + ADDITION_COLOR + str + ANSI_DEFAULT - case Modified(_, str) => ADDITION_COLOR + str + ANSI_DEFAULT - case Deleted(str) if printDiffDel => DELETION_COLOR + str + ANSI_DEFAULT - }.mkString - } - - private sealed trait Patch - private final case class Unmodified(str: String) extends Patch - private final case class Modified(original: String, str: String) extends Patch - private final case class Deleted(str: String) extends Patch - private final case class Inserted(str: String) extends Patch - - private def hirschberg(a: Array[String], b: Array[String]): Array[Patch] = { - def build(x: Array[String], y: Array[String], builder: mutable.ArrayBuilder[Patch]): Unit = { - if (x.isEmpty) { - builder += Inserted(y.mkString) - } else if (y.isEmpty) { - builder += Deleted(x.mkString) - } else if (x.length == 1 || y.length == 1) { - needlemanWunsch(x, y, builder) - } else { - val xlen = x.length - val xmid = xlen / 2 - val ylen = y.length - - val (x1, x2) = x.splitAt(xmid) - val leftScore = nwScore(x1, y) - val rightScore = nwScore(x2.reverse, y.reverse) - val scoreSum = (leftScore zip rightScore.reverse).map { - case (left, right) => left + right - } - val max = scoreSum.max - val ymid = scoreSum.indexOf(max) - - val (y1, y2) = y.splitAt(ymid) - build(x1, y1, builder) - build(x2, y2, builder) - } - } - val builder = Array.newBuilder[Patch] - build(a, b, builder) - builder.result() - } - - private def nwScore(x: Array[String], y: Array[String]): Array[Int] = { - def ins(s: String) = -2 - def del(s: String) = -2 - def sub(s1: String, s2: String) = if (s1 == s2) 2 else -1 - - val score = Array.fill(x.length + 1, y.length + 1)(0) - for (j <- 1 to y.length) - score(0)(j) = score(0)(j - 1) + ins(y(j - 1)) - for (i <- 1 to x.length) { - score(i)(0) = score(i - 1)(0) + del(x(i - 1)) - for (j <- 1 to y.length) { - val scoreSub = score(i - 1)(j - 1) + sub(x(i - 1), y(j - 1)) - val scoreDel = score(i - 1)(j) + del(x(i - 1)) - val scoreIns = score(i)(j - 1) + ins(y(j - 1)) - score(i)(j) = scoreSub max scoreDel max scoreIns - } - } - Array.tabulate(y.length + 1)(j => score(x.length)(j)) - } - - private def needlemanWunsch(x: Array[String], y: Array[String], builder: mutable.ArrayBuilder[Patch]): Unit = { - def similarity(a: String, b: String) = if (a == b) 2 else -1 - val d = 1 - val score = Array.tabulate(x.length + 1, y.length + 1) { (i, j) => - if (i == 0) d * j - else if (j == 0) d * i - else 0 - } - for (i <- 1 to x.length) { - for (j <- 1 to y.length) { - val mtch = score(i - 1)(j - 1) + similarity(x(i - 1), y(j - 1)) - val delete = score(i - 1)(j) + d - val insert = score(i)(j - 1) + d - score(i)(j) = mtch max insert max delete - } - } - - var alignment = List.empty[Patch] - var i = x.length - var j = y.length - while (i > 0 || j > 0) { - if (i > 0 && j > 0 && score(i)(j) == score(i - 1)(j - 1) + similarity(x(i - 1), y(j - 1))) { - val newHead = - if (x(i - 1) == y(j - 1)) Unmodified(x(i - 1)) - else Modified(x(i - 1), y(j - 1)) - alignment = newHead :: alignment - i = i - 1 - j = j - 1 - } else if (i > 0 && score(i)(j) == score(i - 1)(j) + d) { - alignment = Deleted(x(i - 1)) :: alignment - i = i - 1 - } else { - alignment = Inserted(y(j - 1)) :: alignment - j = j - 1 - } - } - builder ++= alignment - } - -} diff --git a/src/dotty/tools/dotc/util/DotClass.scala b/src/dotty/tools/dotc/util/DotClass.scala deleted file mode 100644 index cdb697a45..000000000 --- a/src/dotty/tools/dotc/util/DotClass.scala +++ /dev/null @@ -1,12 +0,0 @@ -package dotty.tools.dotc.util - -/** Adds standard functionality to a class. - * For now: Just the `unsupported` method. - */ -class DotClass { - - /** Throws an `UnsupportedOperationException` with the given method name. */ - def unsupported(methodName: String): Nothing = - throw new UnsupportedOperationException(s"$getClass.$methodName") - -} diff --git a/src/dotty/tools/dotc/util/FreshNameCreator.scala b/src/dotty/tools/dotc/util/FreshNameCreator.scala deleted file mode 100644 index 521947895..000000000 --- a/src/dotty/tools/dotc/util/FreshNameCreator.scala +++ /dev/null @@ -1,33 +0,0 @@ -package dotty.tools -package dotc -package util - -import scala.collection.mutable - -trait FreshNameCreator { - def newName(prefix: String = ""): String - - @deprecated("use newName(prefix)", "2.9.0") - def newName(pos: scala.reflect.internal.util.Position, prefix: String): String = newName(prefix) - @deprecated("use newName()", "2.9.0") - def newName(pos: scala.reflect.internal.util.Position): String = newName() -} - -object FreshNameCreator { - class Default extends FreshNameCreator { - protected var counter = 0 - protected val counters = mutable.AnyRefMap[String, Int]() withDefaultValue 0 - - /** - * Create a fresh name with the given prefix. It is guaranteed - * that the returned name has never been returned by a previous - * call to this function (provided the prefix does not end in a digit). - */ - def newName(prefix: String): String = { - val safePrefix = prefix.replaceAll("""[<>]""", """\$""") - counters(safePrefix) += 1 - val counter = counters(safePrefix) - if (prefix.isEmpty) "$" + counter + "$" else safePrefix + counter - } - } -} diff --git a/src/dotty/tools/dotc/util/HashSet.scala b/src/dotty/tools/dotc/util/HashSet.scala deleted file mode 100644 index ff470ef5d..000000000 --- a/src/dotty/tools/dotc/util/HashSet.scala +++ /dev/null @@ -1,146 +0,0 @@ -package dotty.tools.dotc.util - -/** A hash set that allows some privileged protected access to its internals - */ -class HashSet[T >: Null <: AnyRef](initialCapacity: Int, loadFactor: Float = 0.25f) extends Set[T] { - private var used: Int = _ - private var limit: Int = _ - private var table: Array[AnyRef] = _ - - clear() - - /** The number of elements in the set */ - def size: Int = used - - private def allocate(size: Int) = { - table = new Array[AnyRef](size) - limit = (size * loadFactor).toInt - } - - /** Remove all elements from this set and set back to initial configuration */ - def clear(): Unit = { - used = 0 - allocate(initialCapacity) - } - - /** Turn hashode `x` into a table index */ - private def index(x: Int): Int = math.abs(x % table.length) - - /** Hashcode, can be overridden */ - def hash(x: T): Int = x.hashCode - - /** Find entry such that `x equals entry`. If it exists, return it. - * If not, enter `x` in set and return `x`. - */ - def findEntryOrUpdate(x: T): T = { - var h = index(hash(x)) - var entry = table(h) - while (entry ne null) { - if (x equals entry) return entry.asInstanceOf[T] - h = index(h + 1) - entry = table(h) - } - addEntryAt(h, x) - } - - /** Add entry at `x` at index `idx` */ - private def addEntryAt(idx: Int, x: T) = { - table(idx) = x - used += 1 - if (used > limit) growTable() - x - } - - /** The entry in the set such that `x equals entry`, or else `null`. */ - def findEntry(x: T): T = { - var h = index(hash(x)) - var entry = table(h) - while ((entry ne null) && !(x equals entry)) { - h = index(h + 1) - entry = table(h) - } - entry.asInstanceOf[T] - } - - private var rover: Int = -1 - - /** Add entry `x` to set */ - def addEntry(x: T): Unit = { - var h = index(hash(x)) - var entry = table(h) - while (entry ne null) { - if (x equals entry) return - h = index(h + 1) - entry = table(h) - } - table(h) = x - used += 1 - if (used > (table.length >> 2)) growTable() - } - - /** Add all entries in `xs` to set */ - def addEntries(xs: TraversableOnce[T]): Unit = { - xs foreach addEntry - } - - /** The iterator of all elements in the set */ - def iterator = new Iterator[T] { - private var i = 0 - def hasNext: Boolean = { - while (i < table.length && (table(i) eq null)) i += 1 - i < table.length - } - def next(): T = - if (hasNext) { i += 1; table(i - 1).asInstanceOf[T] } - else null - } - - /** Privileged access: Find first entry with given hashcode */ - protected def findEntryByHash(hashCode: Int): T = { - rover = index(hashCode) - nextEntryByHash(hashCode) - } - - /** Privileged access: Find next entry with given hashcode. Needs to immediately - * follow a `findEntryByhash` or `nextEntryByHash` operation. - */ - protected def nextEntryByHash(hashCode: Int): T = { - var entry = table(rover) - while (entry ne null) { - rover = index(rover + 1) - if (hash(entry.asInstanceOf[T]) == hashCode) return entry.asInstanceOf[T] - entry = table(rover) - } - null - } - - /** Privileged access: Add entry `x` at the last position where an unsuccsessful - * `findEntryByHash` or `nextEntryByhash` operation returned. Needs to immediately - * follow a `findEntryByhash` or `nextEntryByHash` operation that was unsucessful, - * i.e. that returned `null`. - */ - protected def addEntryAfterScan(x: T): T = addEntryAt(rover, x) - - private def addOldEntry(x: T): Unit = { - var h = index(hash(x)) - var entry = table(h) - while (entry ne null) { - h = index(h + 1) - entry = table(h) - } - table(h) = x - } - - private def growTable(): Unit = { - val oldtable = table - allocate(table.length * 2) - var i = 0 - while (i < oldtable.length) { - val entry = oldtable(i) - if (entry ne null) addOldEntry(entry.asInstanceOf[T]) - i += 1 - } - } - - override def toString() = "HashSet(%d / %d)".format(used, table.length) -} diff --git a/src/dotty/tools/dotc/util/LRUCache.scala b/src/dotty/tools/dotc/util/LRUCache.scala deleted file mode 100644 index 5f53e81c4..000000000 --- a/src/dotty/tools/dotc/util/LRUCache.scala +++ /dev/null @@ -1,100 +0,0 @@ -package dotty.tools.dotc.util - -import reflect.ClassTag -import annotation.tailrec - -/** A least-recently-used cache for Key -> Value computations - * It currently keeps the last 8 associations, but this can be - * changed to anywhere between 2 and 16 by changing `LRUCache.Retained`. - * - * Implementation: We keep a ring of eight places, linked - * with the `next` data structure. The ring models a priority queue. - * `last` points to the last element of the queue, and - * `next(last)` to the first one. Lookups compare keys - * sequentially from first to last. Elements with successful lookup - * get promoted to be first in the queue. Elements are evicted - * at the `last` position. - */ -class LRUCache[Key >: Null <: AnyRef : ClassTag, Value >: Null: ClassTag] { - import LRUCache._ - val keys = new Array[Key](Retained) - val values = new Array[Value](Retained) - var next = new SixteenNibbles(initialRing.bits) - var last = Retained - 1 // value is arbitrary - var lastButOne: Int = last - 1 - - def first = next(last) - - /** Lookup key, returning value or `null` for not found. - * As a side effect, sets `lastButOne` to the element before `last` - * if key was not found. - */ - def lookup(key: Key): Value = { - @tailrec - def lookupNext(prev: Int, current: Int, nx: SixteenNibbles): Value = { - val follow = nx(current) - if (keys(current) eq key) { - // arrange so that found element is at position `first`. - if (current == last) last = prev - else if (prev != last) { - next = next.updated(prev, follow) - next = next.updated(current, first) - next = next.updated(last, current) - } - values(current) - } else if (current == last) { - lastButOne = prev - null - } else - lookupNext(current, follow, nx) - } - lookupNext(last, first, next) - } - - /** Enter key/value in cache at position `last`. - * As a side effect, sets `last` to `lastButOne`. - * If `lastButOne` was set by a preceding unsuccessful `lookup` - * for the same key, this means that the new element is now the - * first in the queue. If there was no preceding lookup, the element - * is inserted at a random position in the queue. - */ - def enter(key: Key, value: Value): Unit = { - keys(last) = key - values(last) = value - last = lastButOne - } - - /** Invalidate key. The invalidated element becomes - * the last in the queue. - */ - def invalidate(key: Key): Unit = - if (lookup(key) != null) { - keys(first) = null - last = first - } - - def indices: Iterator[Int] = Iterator.iterate(first)(next.apply) - - def keysIterator: Iterator[Key] = - indices take Retained map keys filter (_ != null) - - override def toString = { - val assocs = keysIterator - .toList // double reverse so that lookups do not perturb order - .reverse - .map(key => s"$key -> ${lookup(key)}") - .reverse - s"LRUCache(${assocs.mkString(", ")})" - } -} - -object LRUCache { - - /** The number of retained elements in the cache; must be at most 16. */ - val Retained = 16 - - /** The initial ring: 0 -> 1 -> ... -> 7 -> 0 */ - val initialRing = - (new SixteenNibbles(0L) /: (0 until Retained))((nibbles, idx) => - nibbles.updated(idx, (idx + 1) % Retained)) -} diff --git a/src/dotty/tools/dotc/util/NameTransformer.scala b/src/dotty/tools/dotc/util/NameTransformer.scala deleted file mode 100644 index 330d513fe..000000000 --- a/src/dotty/tools/dotc/util/NameTransformer.scala +++ /dev/null @@ -1,163 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package dotty.tools.dotc -package util - -import core.Names._ -import core.Decorators._ - -/** Provides functions to encode and decode Scala symbolic names. - * Also provides some constants. - */ -object NameTransformer { - // XXX Short term: providing a way to alter these without having to recompile - // the compiler before recompiling the compiler. - val MODULE_SUFFIX_STRING = sys.props.getOrElse("SCALA_MODULE_SUFFIX_STRING", "$") - val NAME_JOIN_STRING = sys.props.getOrElse("SCALA_NAME_JOIN_STRING", "$") - val MODULE_INSTANCE_NAME = "MODULE$" - - private val nops = 128 - private val ncodes = 26 * 26 - - private class OpCodes(val op: Char, val code: String, val next: OpCodes) - - private val op2code = new Array[String](nops) - private val code2op = new Array[OpCodes](ncodes) - private def enterOp(op: Char, code: String) = { - op2code(op) = code - val c = (code.charAt(1) - 'a') * 26 + code.charAt(2) - 'a' - code2op(c) = new OpCodes(op, code, code2op(c)) - } - - /* Note: decoding assumes opcodes are only ever lowercase. */ - enterOp('~', "$tilde") - enterOp('=', "$eq") - enterOp('<', "$less") - enterOp('>', "$greater") - enterOp('!', "$bang") - enterOp('#', "$hash") - enterOp('%', "$percent") - enterOp('^', "$up") - enterOp('&', "$amp") - enterOp('|', "$bar") - enterOp('*', "$times") - enterOp('/', "$div") - enterOp('+', "$plus") - enterOp('-', "$minus") - enterOp(':', "$colon") - enterOp('\\', "$bslash") - enterOp('?', "$qmark") - enterOp('@', "$at") - - /** Replace operator symbols by corresponding `\$opname`. - * - * @param name the string to encode - * @return the string with all recognized opchars replaced with their encoding - */ - def encode[N <: Name](name: N): N = { - var buf: StringBuilder = null - val len = name.length - var i = 0 - while (i < len) { - val c = name(i) - if (c < nops && (op2code(c) ne null)) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.slice(0, i)) - } - buf.append(op2code(c)) - /* Handle glyphs that are not valid Java/JVM identifiers */ - } - else if (!Character.isJavaIdentifierPart(c)) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.slice(0, i)) - } - buf.append("$u%04X".format(c.toInt)) - } - else if (buf ne null) { - buf.append(c) - } - i += 1 - } - if (buf eq null) name - else if (name.isTermName) buf.toString.toTermName.asInstanceOf[N] - else buf.toString.toTypeName.asInstanceOf[N] - } - - /** Replace `\$opname` by corresponding operator symbol. - * - * @param name0 the string to decode - * @return the string with all recognized operator symbol encodings replaced with their name - */ - def decode(name0: String): String = { - //System.out.println("decode: " + name);//DEBUG - val name = if (name0.endsWith("<init>")) name0.substring(0, name0.length() - ("<init>").length()) + "this" - else name0 - var buf: StringBuilder = null - val len = name.length() - var i = 0 - while (i < len) { - var ops: OpCodes = null - var unicode = false - val c = name charAt i - if (c == '$' && i + 2 < len) { - val ch1 = name.charAt(i + 1) - if ('a' <= ch1 && ch1 <= 'z') { - val ch2 = name.charAt(i + 2) - if ('a' <= ch2 && ch2 <= 'z') { - ops = code2op((ch1 - 'a') * 26 + ch2 - 'a') - while ((ops ne null) && !name.startsWith(ops.code, i)) ops = ops.next - if (ops ne null) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.substring(0, i)) - } - buf.append(ops.op) - i += ops.code.length() - } - /* Handle the decoding of Unicode glyphs that are - * not valid Java/JVM identifiers */ - } else if ((len - i) >= 6 && // Check that there are enough characters left - ch1 == 'u' && - ((Character.isDigit(ch2)) || - ('A' <= ch2 && ch2 <= 'F'))) { - /* Skip past "$u", next four should be hexadecimal */ - val hex = name.substring(i + 2, i + 6) - try { - val str = Integer.parseInt(hex, 16).toChar - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.substring(0, i)) - } - buf.append(str) - /* 2 for "$u", 4 for hexadecimal number */ - i += 6 - unicode = true - } catch { - case _:NumberFormatException => - /* `hex` did not decode to a hexadecimal number, so - * do nothing. */ - } - } - } - } - /* If we didn't see an opcode or encoded Unicode glyph, and the - buffer is non-empty, write the current character and advance - one */ - if ((ops eq null) && !unicode) { - if (buf ne null) - buf.append(c) - i += 1 - } - } - //System.out.println("= " + (if (buf == null) name else buf.toString()));//DEBUG - if (buf eq null) name else buf.toString() - } -} diff --git a/src/dotty/tools/dotc/util/Positions.scala b/src/dotty/tools/dotc/util/Positions.scala deleted file mode 100644 index c3890cc9a..000000000 --- a/src/dotty/tools/dotc/util/Positions.scala +++ /dev/null @@ -1,173 +0,0 @@ -package dotty.tools.dotc -package util -import language.implicitConversions - -/** Position format in little endian: - * Start: unsigned 26 Bits (works for source files up to 64M) - * End: unsigned 26 Bits - * Point: unsigned 12 Bits relative to start - * NoPosition encoded as -1L (this is a normally invalid position - * because point would lie beyond end. - */ -object Positions { - - private val StartEndBits = 26 - val StartEndMask: Long = (1L << StartEndBits) - 1 - private val SyntheticPointDelta = (1 << (64 - StartEndBits * 2)) - 1 - - /** The maximal representable offset in a position */ - val MaxOffset = StartEndMask - - /** Convert offset `x` to an integer by sign extending the original - * field of `StartEndBits` width. - */ - def offsetToInt(x: Int) = - x << (32 - StartEndBits) >> (32 - StartEndBits) - - /** A position indicates a range between a start offset and an end offset. - * Positions can be synthetic or source-derived. A source-derived position - * has in addition a point lies somewhere between start and end. The point - * is roughly where the ^ would go if an error was diagnosed at that position. - * All quantities are encoded opaquely in a Long. - */ - class Position(val coords: Long) extends AnyVal { - - /** Is this position different from NoPosition? */ - def exists = this != NoPosition - - /** The start of this position. */ - def start: Int = { - assert(exists) - (coords & StartEndMask).toInt - } - - /** The end of this position */ - def end: Int = { - assert(exists) - ((coords >>> StartEndBits) & StartEndMask).toInt - } - - /** The point of this position, returns start for synthetic positions */ - def point: Int = { - assert(exists) - val poff = pointDelta - if (poff == SyntheticPointDelta) start else start + poff - } - - /** The difference between point and start in this position */ - def pointDelta = - (coords >>> (StartEndBits * 2)).toInt - - def orElse(that: Position) = - if (this.exists) this else that - - /** The union of two positions. This is the least range that encloses - * both positions. It is always a synthetic position. - */ - def union(that: Position) = - if (!this.exists) that - else if (!that.exists) this - else Position(this.start min that.start, this.end max that.end, this.point) - - /** Does the range of this position contain the one of that position? */ - def contains(that: Position): Boolean = - !that.exists || exists && (start <= that.start && end >= that.end) - - /** Is this position synthetic? */ - def isSynthetic = pointDelta == SyntheticPointDelta - - /** Is this position source-derived? */ - def isSourceDerived = !isSynthetic - - /** A position where all components are shifted by a given `offset` - * relative to this position. - */ - def shift(offset: Int) = - if (exists) fromOffsets(start + offset, end + offset, pointDelta) - else this - - /** The zero-extent position with start and end at the point of this position */ - def focus = if (exists) Position(point) else NoPosition - - /** The zero-extent position with start and end at the start of this position */ - def startPos = if (exists) Position(start) else NoPosition - - /** The zero-extent position with start and end at the end of this position */ - def endPos = if (exists) Position(end) else NoPosition - - /** A copy of this position with a different start */ - def withStart(start: Int) = - fromOffsets(start, this.end, if (isSynthetic) SyntheticPointDelta else this.point - start) - - /** A copy of this position with a different end */ - def withEnd(end: Int) = fromOffsets(this.start, end, pointDelta) - - /** A copy of this position with a different point */ - def withPoint(point: Int) = fromOffsets(this.start, this.end, point - this.start) - - /** A synthetic copy of this position */ - def toSynthetic = if (isSynthetic) this else Position(start, end) - - override def toString = { - val (left, right) = if (isSynthetic) ("<", ">") else ("[", "]") - if (exists) - s"$left$start..${if (point == start) "" else s"$point.."}$end$right" - else - s"${left}no position${right}" - } - } - - private def fromOffsets(start: Int, end: Int, pointDelta: Int) = { - //assert(start <= end || start == 1 && end == 0, s"$start..$end") - new Position( - (start & StartEndMask).toLong | - ((end & StartEndMask).toLong << StartEndBits) | - (pointDelta.toLong << (StartEndBits * 2))) - } - - /** A synthetic position with given start and end */ - def Position(start: Int, end: Int): Position = { - val pos = fromOffsets(start, end, SyntheticPointDelta) - assert(pos.isSynthetic) - pos - } - - /** A source-derived position with given start, end, and point delta */ - def Position(start: Int, end: Int, point: Int): Position = { - val pointDelta = (point - start) max 0 - val pos = fromOffsets(start, end, if (pointDelta >= SyntheticPointDelta) 0 else pointDelta) - assert(pos.isSourceDerived) - pos - } - - /** A synthetic zero-extent position that starts and ends at given `start`. */ - def Position(start: Int): Position = Position(start, start) - - /** A sentinel for a non-existing position */ - val NoPosition = Position(1, 0) - - /** The coordinate of a symbol. This is either an index or - * a zero-range position. - */ - class Coord(val encoding: Int) extends AnyVal { - def isIndex = encoding > 0 - def isPosition = encoding <= 0 - def toIndex: Int = { - assert(isIndex) - encoding - 1 - } - def toPosition = { - assert(isPosition) - if (this == NoCoord) NoPosition else Position(-1 - encoding) - } - } - - /** An index coordinate */ - implicit def indexCoord(n: Int): Coord = new Coord(n + 1) - implicit def positionCoord(pos: Position): Coord = - if (pos.exists) new Coord(-(pos.point + 1)) - else NoCoord - - /** A sentinel for a missing coordinate */ - val NoCoord = new Coord(0) -} diff --git a/src/dotty/tools/dotc/util/Property.scala b/src/dotty/tools/dotc/util/Property.scala deleted file mode 100644 index 608fc88e6..000000000 --- a/src/dotty/tools/dotc/util/Property.scala +++ /dev/null @@ -1,10 +0,0 @@ -package dotty.tools.dotc.util - -/** Defines a key type with which to tag properties, such as attachments - * or context properties - */ -object Property { - - /** The class of keys for properties of type V */ - class Key[+V] -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/util/Set.scala b/src/dotty/tools/dotc/util/Set.scala deleted file mode 100644 index 3e906c6a8..000000000 --- a/src/dotty/tools/dotc/util/Set.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Martin Odersky - */ -package dotty.tools.dotc.util - -/** A common class for lightweight sets. - */ -abstract class Set[T >: Null] { - - def findEntry(x: T): T - - def addEntry(x: T): Unit - - def iterator: Iterator[T] - - def foreach[U](f: T => U): Unit = iterator foreach f - - def apply(x: T): Boolean = contains(x) - - def contains(x: T): Boolean = - findEntry(x) != null - - def toList = iterator.toList - - def clear: Unit -} diff --git a/src/dotty/tools/dotc/util/ShowPickled.scala b/src/dotty/tools/dotc/util/ShowPickled.scala deleted file mode 100644 index 477449074..000000000 --- a/src/dotty/tools/dotc/util/ShowPickled.scala +++ /dev/null @@ -1,287 +0,0 @@ -package dotty.tools.dotc -package util - -import java.io.{File, FileInputStream, PrintStream} -import java.lang.Long.toHexString -import java.lang.Float.intBitsToFloat -import java.lang.Double.longBitsToDouble -import scala.reflect.internal.Flags -import scala.reflect.internal.pickling.PickleFormat -import core.unpickleScala2.PickleBuffer -import core.Names._ - -object ShowPickled { - import PickleFormat._ - - case class PickleBufferEntry(num: Int, startIndex: Int, tag: Int, bytes: Array[Byte]) { - def isName = tag == TERMname || tag == TYPEname - def hasName = tag match { - case TYPEsym | ALIASsym | CLASSsym | MODULEsym | VALsym | EXTref | EXTMODCLASSref => true - case _ => false - } - def readName = - if (isName) new String(bytes, "UTF-8") - else sys.error("%s is no name" format tagName) - def nameIndex = - if (hasName) readNat(bytes, 0) - else sys.error("%s has no name" format tagName) - - def tagName = tag2string(tag) - override def toString = "%d,%d: %s".format(num, startIndex, tagName) - } - - case class PickleBufferEntryList(entries: IndexedSeq[PickleBufferEntry]) { - def nameAt(idx: Int) = { - val entry = entries(idx) - if (entry.isName) entry.readName - else if (entry.hasName) entries(entry.nameIndex).readName - else "?" - } - } - - def makeEntryList(buf: PickleBuffer, index: Array[Int]) = { - val entries = buf.toIndexedSeq.zipWithIndex map { - case ((tag, data), num) => PickleBufferEntry(num, index(num), tag, data) - } - - PickleBufferEntryList(entries) - } - - def tag2string(tag: Int): String = tag match { - case TERMname => "TERMname" - case TYPEname => "TYPEname" - case NONEsym => "NONEsym" - case TYPEsym => "TYPEsym" - case ALIASsym => "ALIASsym" - case CLASSsym => "CLASSsym" - case MODULEsym => "MODULEsym" - case VALsym => "VALsym" - case EXTref => "EXTref" - case EXTMODCLASSref => "EXTMODCLASSref" - case NOtpe => "NOtpe" - case NOPREFIXtpe => "NOPREFIXtpe" - case THIStpe => "THIStpe" - case SINGLEtpe => "SINGLEtpe" - case CONSTANTtpe => "CONSTANTtpe" - case TYPEREFtpe => "TYPEREFtpe" - case TYPEBOUNDStpe => "TYPEBOUNDStpe" - case REFINEDtpe => "REFINEDtpe" - case CLASSINFOtpe => "CLASSINFOtpe" - case METHODtpe => "METHODtpe" - case POLYtpe => "POLYtpe" - case IMPLICITMETHODtpe => "METHODtpe" // IMPLICITMETHODtpe no longer used. - case SUPERtpe => "SUPERtpe" - case LITERALunit => "LITERALunit" - case LITERALboolean => "LITERALboolean" - case LITERALbyte => "LITERALbyte" - case LITERALshort => "LITERALshort" - case LITERALchar => "LITERALchar" - case LITERALint => "LITERALint" - case LITERALlong => "LITERALlong" - case LITERALfloat => "LITERALfloat" - case LITERALdouble => "LITERALdouble" - case LITERALstring => "LITERALstring" - case LITERALnull => "LITERALnull" - case LITERALclass => "LITERALclass" - case LITERALenum => "LITERALenum" - case SYMANNOT => "SYMANNOT" - case CHILDREN => "CHILDREN" - case ANNOTATEDtpe => "ANNOTATEDtpe" - case ANNOTINFO => "ANNOTINFO" - case ANNOTARGARRAY => "ANNOTARGARRAY" - // case DEBRUIJNINDEXtpe => "DEBRUIJNINDEXtpe" - case EXISTENTIALtpe => "EXISTENTIALtpe" - case TREE => "TREE" - case MODIFIERS => "MODIFIERS" - - case _ => "***BAD TAG***(" + tag + ")" - } - - /** Extremely regrettably, essentially copied from PickleBuffer. - */ - def readNat(data: Array[Byte], index: Int): Int = { - var idx = index - var result = 0L - var b = 0L - do { - b = data(idx) - idx += 1 - result = (result << 7) + (b & 0x7f) - } while((b & 0x80) != 0L) - - result.toInt - } - - def printFile(buf: PickleBuffer, out: PrintStream = System.out): Unit = { - out.println("Version " + buf.readNat() + "." + buf.readNat()) - val index = buf.createIndex - val entryList = makeEntryList(buf, index) - buf.readIndex = 0 - - def p(s: String) = out print s - - def printNameRef(): Unit = { - val idx = buf.readNat() - val name = entryList nameAt idx - val toPrint = " %s(%s)".format(idx, name) - - out print toPrint - } - - def printNat() = p(" " + buf.readNat()) - def printReadNat(x: Int) = p(" " + x) - - def printSymbolRef() = printNat() - def printTypeRef() = printNat() - def printConstantRef() = printNat() - def printAnnotInfoRef() = printNat() - def printConstAnnotArgRef() = printNat() - def printAnnotArgRef() = printNat() - - def printSymInfo(end: Int, isType: Boolean): Unit = { - printNameRef() - printSymbolRef() - val pflags = buf.readLongNat() - def printFlags(privateWithin: Option[Int]) = { - val accessBoundary = ( - for (idx <- privateWithin) yield { - val s = entryList nameAt idx - idx + "(" + s + ")" - } - ) - val flagString = PickleBuffer.unpickleScalaFlags(pflags, isType).toString - out.print(" %s[%s]".format(toHexString(pflags), flagString)) - } - - /** Might be info or privateWithin */ - val x = buf.readNat() - if (buf.readIndex == end) { - printFlags(None) - printReadNat(x) - } - else { - printFlags(Some(x)) - printTypeRef() - } - } - - /** Note: the entries which require some semantic analysis to be correctly - * interpreted are for the most part going to tell you the wrong thing. - * It's not so easy to duplicate the logic applied in the UnPickler. - */ - def printEntry(i: Int): Unit = { - buf.readIndex = index(i) - p(i + "," + buf.readIndex + ": ") - val tag = buf.readByte() - out.print(tag2string(tag)) - val len = buf.readNat() - val end = len + buf.readIndex - p(" " + len + ":") - tag match { - case TERMname => - out.print(" ") - out.print(termName(buf.bytes, buf.readIndex, len).toString) - buf.readIndex = end - case TYPEname => - out.print(" ") - out.print(typeName(buf.bytes, buf.readIndex, len)) - buf.readIndex = end - case TYPEsym | ALIASsym | CLASSsym | MODULEsym | VALsym => - printSymInfo(end, tag == TYPEsym || tag == ALIASsym || tag == CLASSsym) - if (tag == CLASSsym && (buf.readIndex < end)) printTypeRef() - case EXTref | EXTMODCLASSref => - printNameRef() - if (buf.readIndex < end) { printSymbolRef() } - case THIStpe => - printSymbolRef() - case SINGLEtpe => - printTypeRef(); printSymbolRef() - case CONSTANTtpe => - printTypeRef(); printConstantRef() - case TYPEREFtpe => - printTypeRef(); printSymbolRef(); buf.until(end, printTypeRef) - case TYPEBOUNDStpe => - printTypeRef(); printTypeRef() - case REFINEDtpe => - printSymbolRef(); buf.until(end, printTypeRef) - case CLASSINFOtpe => - printSymbolRef(); buf.until(end, printTypeRef) - case METHODtpe | IMPLICITMETHODtpe => - printTypeRef(); buf.until(end, printTypeRef) - case POLYtpe => - printTypeRef(); buf.until(end, printSymbolRef) - case LITERALboolean => - out.print(if (buf.readLong(len) == 0L) " false" else " true") - case LITERALbyte => - out.print(" " + buf.readLong(len).toByte) - case LITERALshort => - out.print(" " + buf.readLong(len).toShort) - case LITERALchar => - out.print(" " + buf.readLong(len).toChar) - case LITERALint => - out.print(" " + buf.readLong(len).toInt) - case LITERALlong => - out.print(" " + buf.readLong(len)) - case LITERALfloat => - out.print(" " + intBitsToFloat(buf.readLong(len).toInt)) - case LITERALdouble => - out.print(" " + longBitsToDouble(buf.readLong(len))) - case LITERALstring => - printNameRef() - case LITERALenum => - printSymbolRef() - case LITERALnull => - out.print(" <null>") - case LITERALclass => - printTypeRef() - case CHILDREN => - printSymbolRef(); buf.until(end, printSymbolRef) - case SYMANNOT => - printSymbolRef(); printTypeRef(); buf.until(end, printAnnotArgRef) - case ANNOTATEDtpe => - printTypeRef(); buf.until(end, printAnnotInfoRef) - case ANNOTINFO => - printTypeRef(); buf.until(end, printAnnotArgRef) - case ANNOTARGARRAY => - buf.until(end, printConstAnnotArgRef) - case EXISTENTIALtpe => - printTypeRef(); buf.until(end, printSymbolRef) - - case _ => - } - out.println() - if (buf.readIndex != end) { - out.println("BAD ENTRY END: computed = %d, actual = %d, bytes = %s".format( - end, buf.readIndex, buf.bytes.slice(index(i), (end max buf.readIndex)).mkString(", ") - )) - } - } - - for (i <- 0 until index.length) printEntry(i) - } - -/* - * - def fromFile(path: String) = fromBytes(io.File(path).toByteArray) - def fromName(name: String) = fromBytes(scalaSigBytesForPath(name) getOrElse Array()) - def fromBytes(data: => Array[Byte]): Option[PickleBuffer] = - try Some(new PickleBuffer(data, 0, data.length)) - catch { case _: Exception => None } - - def show(what: String, pickle: PickleBuffer) = { - Console.println(what) - val saved = pickle.readIndex - pickle.readIndex = 0 - printFile(pickle, Console.out) - pickle.readIndex = saved - } - - def main(args: Array[String]) { - args foreach { arg => - (fromFile(arg) orElse fromName(arg)) match { - case Some(pb) => show(arg + ":", pb) - case _ => Console.println("Cannot read " + arg) - } - } - }*/ -} diff --git a/src/dotty/tools/dotc/util/SimpleMap.scala b/src/dotty/tools/dotc/util/SimpleMap.scala deleted file mode 100644 index b8668d7e4..000000000 --- a/src/dotty/tools/dotc/util/SimpleMap.scala +++ /dev/null @@ -1,223 +0,0 @@ -package dotty.tools.dotc.util - -import collection.mutable.ListBuffer - -abstract class SimpleMap[K <: AnyRef, +V >: Null <: AnyRef] extends (K => V) { - def size: Int - def apply(k: K): V - def remove(k: K): SimpleMap[K, V] - def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleMap[K, V1] - def contains(k: K): Boolean = apply(k) != null - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleMap[K, V1] - def foreachBinding(f: (K, V) => Unit): Unit - def map2[T](f: (K, V) => T): List[T] = { - val buf = new ListBuffer[T] - foreachBinding((k, v) => buf += f(k, v)) - buf.toList - } - def keys: List[K] = map2((k, v) => k) - def toList: List[(K, V)] = map2((k, v) => (k, v)) - override def toString = { - def assocToString(key: K, value: V) = s"$key -> $value" - map2(assocToString) mkString ("(", ", ", ")") - } -} - -object SimpleMap { - - private val CompactifyThreshold = 4 - - private object myEmpty extends SimpleMap[AnyRef, Null] { - def size = 0 - def apply(k: AnyRef) = null - def remove(k: AnyRef) = this - def updated[V1 >: Null <: AnyRef](k: AnyRef, v: V1) = new Map1(k, v) - def mapValuesNow[V1 >: Null <: AnyRef](f: (AnyRef, V1) => V1) = this - def foreachBinding(f: (AnyRef, Null) => Unit) = () - } - - def Empty[K <: AnyRef] = myEmpty.asInstanceOf[SimpleMap[K, Null]] - - class Map1[K <: AnyRef, +V >: Null <: AnyRef] (k1: K, v1: V) extends SimpleMap[K, V] { - def size = 1 - def apply(k: K) = - if (k == k1) v1 - else null - def remove(k: K) = - if (k == k1) Empty.asInstanceOf[SimpleMap[K, V]] - else this - def updated[V1 >: V <: AnyRef](k: K, v: V1) = - if (k == k1) new Map1(k, v) - else new Map2(k1, v1, k, v) - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { - val w1 = f(k1, v1) - if (v1 eq w1) this else new Map1(k1, w1) - } - def foreachBinding(f: (K, V) => Unit) = f(k1, v1) - } - - class Map2[K <: AnyRef, +V >: Null <: AnyRef] (k1: K, v1: V, k2: K, v2: V) extends SimpleMap[K, V] { - def size = 2 - def apply(k: K) = - if (k == k1) v1 - else if (k == k2) v2 - else null - def remove(k: K) = - if (k == k1) new Map1(k2, v2) - else if (k == k2) new Map1(k1, v1) - else this - def updated[V1 >: V <: AnyRef](k: K, v: V1) = - if (k == k1) new Map2(k, v, k2, v2) - else if (k == k2) new Map2(k1, v1, k, v) - else new Map3(k1, v1, k2, v2, k, v) - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { - val w1 = f(k1, v1); val w2 = f(k2, v2) - if ((v1 eq w1) && (v2 eq w2)) this - else new Map2(k1, w1, k2, w2) - } - def foreachBinding(f: (K, V) => Unit) = { f(k1, v1); f(k2, v2) } - } - - class Map3[K <: AnyRef, +V >: Null <: AnyRef] (k1: K, v1: V, k2: K, v2: V, k3: K, v3: V) extends SimpleMap[K, V] { - def size = 3 - def apply(k: K) = - if (k == k1) v1 - else if (k == k2) v2 - else if (k == k3) v3 - else null - def remove(k: K) = - if (k == k1) new Map2(k2, v2, k3, v3) - else if (k == k2) new Map2(k1, v1, k3, v3) - else if (k == k3) new Map2(k1, v1, k2, v2) - else this - def updated[V1 >: V <: AnyRef](k: K, v: V1) = - if (k == k1) new Map3(k, v, k2, v2, k3, v3) - else if (k == k2) new Map3(k1, v1, k, v, k3, v3) - else if (k == k3) new Map3(k1, v1, k2, v2, k, v) - else new Map4(k1, v1, k2, v2, k3, v3, k, v) - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { - val w1 = f(k1, v1); val w2 = f(k2, v2); val w3 = f(k3, v3) - if ((v1 eq w1) && (v2 eq w2) && (v3 eq w3)) this - else new Map3(k1, w1, k2, w2, k3, w3) - } - def foreachBinding(f: (K, V) => Unit) = { f(k1, v1); f(k2, v2); f(k3, v3) } - } - - class Map4[K <: AnyRef, +V >: Null <: AnyRef] (k1: K, v1: V, k2: K, v2: V, k3: K, v3: V, k4: K, v4: V) extends SimpleMap[K, V] { - def size = 4 - def apply(k: K) = - if (k == k1) v1 - else if (k == k2) v2 - else if (k == k3) v3 - else if (k == k4) v4 - else null - def remove(k: K) = - if (k == k1) new Map3(k2, v2, k3, v3, k4, v4) - else if (k == k2) new Map3(k1, v1, k3, v3, k4, v4) - else if (k == k3) new Map3(k1, v1, k2, v2, k4, v4) - else if (k == k4) new Map3(k1, v1, k2, v2, k3, v3) - else this - def updated[V1 >: V <: AnyRef](k: K, v: V1) = - if (k == k1) new Map4(k, v, k2, v2, k3, v3, k4, v4) - else if (k == k2) new Map4(k1, v1, k, v, k3, v3, k4, v4) - else if (k == k3) new Map4(k1, v1, k2, v2, k, v, k4, v4) - else if (k == k4) new Map4(k1, v1, k2, v2, k3, v3, k, v) - else new MapMore(Array[AnyRef](k1, v1, k2, v2, k3, v3, k4, v4, k, v)) - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { - val w1 = f(k1, v1); val w2 = f(k2, v2); val w3 = f(k3, v3); val w4 = f(k4, v4) - if ((v1 eq w1) && (v2 eq w2) && (v3 eq w3) && (v4 eq w4)) this - else new Map4(k1, w1, k2, w2, k3, w3, k4, w4) - } - def foreachBinding(f: (K, V) => Unit) = { f(k1, v1); f(k2, v2); f(k3, v3); f(k4, v4) } - } - - class MapMore[K <: AnyRef, +V >: Null <: AnyRef](bindings: Array[AnyRef]) extends SimpleMap[K, V] { - private def key(i: Int): K = bindings(i).asInstanceOf[K] - private def value(i: Int): V = bindings(i + 1).asInstanceOf[V] - - def size = bindings.length / 2 - - def apply(k: K): V = { - var i = 0 - while (i < bindings.length) { - if (bindings(i) eq k) return value(i) - i += 2 - } - null - } - - def remove(k: K): SimpleMap[K, V] = { - var i = 0 - while (i < bindings.length) { - if (bindings(i) eq k) return { - if (size == CompactifyThreshold) { - var m: SimpleMap[K, V] = Empty[K] - for (j <- 0 until bindings.length by 2) - if (j != i) m = m.updated(key(j), value(j)) - m - } else { - val bindings1 = new Array[AnyRef](bindings.length - 2) - Array.copy(bindings, 0, bindings1, 0, i) - Array.copy(bindings, i + 2, bindings1, i, bindings1.length - i) - new MapMore(bindings1) - } - } - i += 2 - } - this - } - - def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleMap[K, V] = { - var i = 0 - while (i < bindings.length) { - if (bindings(i) eq k) - return { - if (v eq bindings(i + 1)) this - else { - val bindings1 = bindings.clone - bindings1(i + 1) = v - new MapMore(bindings1) - } - } - i += 2 - } - val bindings2 = new Array[AnyRef](bindings.length + 2) - Array.copy(bindings, 0, bindings2, 0, bindings.length) - bindings2(bindings.length) = k - bindings2(bindings.length + 1) = v - new MapMore(bindings2) - } - - override def contains(k: K): Boolean = { - var i = 0 - while (i < bindings.length) { - if (bindings(i) eq k) return true - i += 2 - } - false - } - - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { - var bindings1: Array[AnyRef] = bindings - var i = 0 - while (i < bindings.length) { - val v = value(i) - val v1 = f(key(i), v) - if ((v1 ne v) && (bindings1 eq bindings)) - bindings1 = bindings.clone - bindings1(i) = bindings(i) - bindings1(i + 1) = v1 - i += 2 - } - if (bindings1 eq bindings) this else new MapMore(bindings1) - } - - def foreachBinding(f: (K, V) => Unit) = { - var i = 0 - while (i < bindings.length) { - f(key(i), value(i)) - i += 2 - } - } - } -} diff --git a/src/dotty/tools/dotc/util/SixteenNibbles.scala b/src/dotty/tools/dotc/util/SixteenNibbles.scala deleted file mode 100644 index 93817604e..000000000 --- a/src/dotty/tools/dotc/util/SixteenNibbles.scala +++ /dev/null @@ -1,28 +0,0 @@ -package dotty.tools.dotc.util - -/** An efficient implementation of sequences of 16 indexed elements with - * values 0..15 in a single Long. - * - */ -class SixteenNibbles(val bits: Long) extends AnyVal { - import SixteenNibbles._ - - def apply(idx: Int): Int = - (bits >>> (idx * Width)).toInt & Mask - - def updated(idx: Int, value: Int): SixteenNibbles = - new SixteenNibbles( - (bits & ~(LongMask << (idx * Width))) | - ((value & Mask).toLong << (idx * Width))) - - def elements: IndexedSeq[Int] = (0 until 16) map apply - - override def toString = - s"SixteenNibbles(${elements.mkString(", ")})" -} - -object SixteenNibbles { - final val Width = 4 - final val Mask = (1 << Width) - 1 - final val LongMask = Mask.toLong -} diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala deleted file mode 100644 index 1d4c9c2ab..000000000 --- a/src/dotty/tools/dotc/util/SourceFile.scala +++ /dev/null @@ -1,145 +0,0 @@ -package dotty.tools -package dotc -package util - -import scala.collection.mutable.ArrayBuffer -import dotty.tools.io._ -import annotation.tailrec -import java.util.regex.Pattern -import java.io.IOException -import Chars._ -import ScriptSourceFile._ -import Positions._ -import scala.io.Codec - -import java.util.Optional - -object ScriptSourceFile { - @sharable private val headerPattern = Pattern.compile("""^(::)?!#.*(\r|\n|\r\n)""", Pattern.MULTILINE) - private val headerStarts = List("#!", "::#!") - - def apply(file: AbstractFile, content: Array[Char]) = { - /** Length of the script header from the given content, if there is one. - * The header begins with "#!" or "::#!" and ends with a line starting - * with "!#" or "::!#". - */ - val headerLength = - if (headerStarts exists (content startsWith _)) { - val matcher = headerPattern matcher content.mkString - if (matcher.find) matcher.end - else throw new IOException("script file does not close its header with !# or ::!#") - } else 0 - new SourceFile(file, content drop headerLength) { - override val underlying = new SourceFile(file, content) - } - } -} - -case class SourceFile(file: AbstractFile, content: Array[Char]) extends interfaces.SourceFile { - - def this(_file: AbstractFile, codec: Codec) = this(_file, new String(_file.toByteArray, codec.charSet).toCharArray) - def this(sourceName: String, cs: Seq[Char]) = this(new VirtualFile(sourceName), cs.toArray) - def this(file: AbstractFile, cs: Seq[Char]) = this(file, cs.toArray) - - /** Tab increment; can be overridden */ - def tabInc = 8 - - override def name = file.name - override def path = file.path - override def jfile = Optional.ofNullable(file.file) - - override def equals(that : Any) = that match { - case that : SourceFile => file.path == that.file.path && start == that.start - case _ => false - } - override def hashCode = file.path.## + start.## - - def apply(idx: Int) = content.apply(idx) - - val length = content.length - - /** true for all source files except `NoSource` */ - def exists: Boolean = true - - /** The underlying source file */ - def underlying: SourceFile = this - - /** The start of this file in the underlying source file */ - def start = 0 - - def atPos(pos: Position): SourcePosition = - if (pos.exists) SourcePosition(underlying, pos) - else NoSourcePosition - - def isSelfContained = underlying eq this - - /** Map a position to a position in the underlying source file. - * For regular source files, simply return the argument. - */ - def positionInUltimateSource(position: SourcePosition): SourcePosition = - SourcePosition(underlying, position.pos shift start) - - private def isLineBreak(idx: Int) = - if (idx >= length) false else { - val ch = content(idx) - // don't identify the CR in CR LF as a line break, since LF will do. - if (ch == CR) (idx + 1 == length) || (content(idx + 1) != LF) - else isLineBreakChar(ch) - } - - private def calculateLineIndices(cs: Array[Char]) = { - val buf = new ArrayBuffer[Int] - buf += 0 - for (i <- 0 until cs.length) if (isLineBreak(i)) buf += i + 1 - buf += cs.length // sentinel, so that findLine below works smoother - buf.toArray - } - private lazy val lineIndices: Array[Int] = calculateLineIndices(content) - - /** Map line to offset of first character in line */ - def lineToOffset(index: Int): Int = lineIndices(index) - - /** A cache to speed up offsetToLine searches to similar lines */ - private var lastLine = 0 - - /** Convert offset to line in this source file - * Lines are numbered from 0 - */ - def offsetToLine(offset: Int): Int = { - lastLine = Util.bestFit(lineIndices, lineIndices.length, offset, lastLine) - lastLine - } - - /** The index of the first character of the line containing position `offset` */ - def startOfLine(offset: Int): Int = { - require(offset >= 0) - lineToOffset(offsetToLine(offset)) - } - - /** The start index of the line following the one containing position `offset` */ - def nextLine(offset: Int): Int = - lineToOffset(offsetToLine(offset) + 1 min lineIndices.length - 1) - - /** The content of the line containing position `offset` */ - def lineContent(offset: Int): String = - content.slice(startOfLine(offset), nextLine(offset)).mkString - - /** The column corresponding to `offset`, starting at 0 */ - def column(offset: Int): Int = { - var idx = startOfLine(offset) - var col = 0 - while (idx != offset) { - col += (if (content(idx) == '\t') (tabInc - col) % tabInc else 1) - idx += 1 - } - col - } - - override def toString = file.toString -} - -@sharable object NoSource extends SourceFile("<no source>", Nil) { - override def exists = false - override def atPos(pos: Position): SourcePosition = NoSourcePosition -} - diff --git a/src/dotty/tools/dotc/util/SourcePosition.scala b/src/dotty/tools/dotc/util/SourcePosition.scala deleted file mode 100644 index aad4995d8..000000000 --- a/src/dotty/tools/dotc/util/SourcePosition.scala +++ /dev/null @@ -1,57 +0,0 @@ -package dotty.tools -package dotc -package util - -import Positions.{Position, NoPosition} - -/** A source position is comprised of a position in a source file */ -case class SourcePosition(source: SourceFile, pos: Position, outer: SourcePosition = NoSourcePosition) -extends interfaces.SourcePosition { - def exists = pos.exists - - def lineContent: String = source.lineContent(point) - - def point: Int = pos.point - /** The line of the position, starting at 0 */ - def line: Int = source.offsetToLine(point) - - /** The lines of the position */ - def lines: List[Int] = - List.range(source.offsetToLine(start), source.offsetToLine(end + 1)) match { - case Nil => line :: Nil - case xs => xs - } - - def lineOffsets: List[Int] = - lines.map(source.lineToOffset(_)) - - def lineContent(lineNumber: Int): String = - source.lineContent(source.lineToOffset(lineNumber)) - - def beforeAndAfterPoint: (List[Int], List[Int]) = - lineOffsets.partition(_ <= point) - - /** The column of the position, starting at 0 */ - def column: Int = source.column(point) - - def start: Int = pos.start - def startLine: Int = source.offsetToLine(start) - def startColumn: Int = source.column(start) - - def end: Int = pos.end - def endLine: Int = source.offsetToLine(end) - def endColumn: Int = source.column(end) - - def withOuter(outer: SourcePosition) = new SourcePosition(source, pos, outer) - - override def toString = - if (source.exists) s"${source.file}:${line + 1}" - else s"(no source file, offset = ${pos.point})" -} - -/** A sentinel for a non-existing source position */ -@sharable object NoSourcePosition extends SourcePosition(NoSource, NoPosition) { - override def toString = "?" - override def withOuter(outer: SourcePosition) = outer -} - diff --git a/src/dotty/tools/dotc/util/Stats.scala b/src/dotty/tools/dotc/util/Stats.scala deleted file mode 100644 index b7e0996f5..000000000 --- a/src/dotty/tools/dotc/util/Stats.scala +++ /dev/null @@ -1,78 +0,0 @@ -package dotty.tools -package dotc -package util - -import core.Contexts._ -import collection.mutable - -@sharable object Stats { - - final val enabled = false - - /** The period in ms in which stack snapshots are displayed */ - final val HeartBeatPeriod = 250 - - var monitored = false - - @volatile private var stack: List[String] = Nil - - val hits = new mutable.HashMap[String, Int] { - override def default(key: String): Int = 0 - } - - @inline - def record(fn: String, n: Int = 1) = - if (enabled) doRecord(fn, n) - - private def doRecord(fn: String, n: Int) = - if (monitored) { - val name = if (fn.startsWith("member-")) "member" else fn - hits(name) += n - } - - @inline - def track[T](fn: String)(op: => T) = - if (enabled) doTrack(fn)(op) else op - - def doTrack[T](fn: String)(op: => T) = - if (monitored) { - stack = fn :: stack - record(fn) - try op - finally stack = stack.tail - } else op - - class HeartBeat extends Thread() { - @volatile private[Stats] var continue = true - - private def printStack(stack: List[String]): Unit = stack match { - case str :: rest => - printStack(rest) - print(s"-> $str ") - case Nil => - println() - print("|") - } - - override final def run(): Unit = { - Thread.sleep(HeartBeatPeriod) - printStack(stack) - if (continue) run() - } - } - - def monitorHeartBeat[T](op: => T)(implicit ctx: Context) = { - if (ctx.settings.Yheartbeat.value) { - var hb = new HeartBeat() - hb.start() - monitored = true - try op - finally { - hb.continue = false - println() - println(hits.toList.sortBy(_._2).map{ case (x, y) => s"$x -> $y" } mkString "\n") - println(s"sizes: ${ctx.base.uniquesSizes}") - } - } else op - } -} diff --git a/src/dotty/tools/dotc/util/Util.scala b/src/dotty/tools/dotc/util/Util.scala deleted file mode 100644 index 0d37f687b..000000000 --- a/src/dotty/tools/dotc/util/Util.scala +++ /dev/null @@ -1,32 +0,0 @@ -package dotty.tools.dotc.util -import reflect.ClassTag - -object Util { - - /** The index `i` in `candidates.indices` such that `candidates(i) <= x` and - * `candidates(i)` is closest to `x`, determined by binary search, or -1 - * if `x < candidates(0)`. - * @param hint If between 0 and `candidates.length` use this - * as the first search point, otherwise use - * `candidates.length/2`. - * @pre candidates is sorted - */ - def bestFit(candidates: Array[Int], length: Int, x: Int, hint: Int = -1): Int = { - def recur(lo: Int, hi: Int, mid: Int): Int = - if (x < candidates(mid)) - recur(lo, mid - 1, (lo + mid - 1) / 2) - else if (mid + 1 < length && x >= candidates(mid + 1)) - recur(mid + 1, hi, (mid + 1 + hi) / 2) - else mid - val initMid = if (0 <= hint && hint < length) hint else length / 2 - if (length == 0 || x < candidates(0)) -1 - else recur(0, length, initMid) - } - - /** An array twice the size of given array, with existing elements copied over */ - def dble[T: ClassTag](arr: Array[T]) = { - val arr1 = new Array[T](arr.length * 2) - Array.copy(arr, 0, arr1, 0, arr.length) - arr1 - } -} diff --git a/src/dotty/tools/dotc/util/common.scala b/src/dotty/tools/dotc/util/common.scala deleted file mode 100644 index d9798aec5..000000000 --- a/src/dotty/tools/dotc/util/common.scala +++ /dev/null @@ -1,14 +0,0 @@ -package dotty.tools.dotc -package util - -import core.Names.Name -import core.Types.WildcardType - -/** Common values hoisted out for performance */ -object common { - - val alwaysTrue = Function.const(true) _ - val alwaysZero = Function.const(0) _ - val alwaysWildcardType = Function.const(WildcardType) _ - -} diff --git a/src/dotty/tools/dotc/util/kwords.sc b/src/dotty/tools/dotc/util/kwords.sc deleted file mode 100644 index 94c17eaf4..000000000 --- a/src/dotty/tools/dotc/util/kwords.sc +++ /dev/null @@ -1,18 +0,0 @@ -package dotty.tools.dotc.util - -import dotty.tools.dotc.parsing._ -import Scanners._ -import Tokens._ - -object kwords { - println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet - keywords.toList.map(tokenString) //> res0: List[String] = List(if, for, else, this, null, new, with, super, case, - //| case class, case object, val, abstract, final, private, protected, override - //| , implicit, var, def, type, extends, true, false, object, class, import, pac - //| kage, yield, do, trait, sealed, throw, try, catch, finally, while, return, m - //| atch, lazy, then, forSome, _, :, =, <-, =>, ';', ';', <:, >:, #, @, <%) - keywords.toList.filter(kw => tokenString(kw) == null) - //> res1: List[Int] = List() - canStartStatTokens contains CASE //> res2: Boolean = false - -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/util/lrutest.sc b/src/dotty/tools/dotc/util/lrutest.sc deleted file mode 100644 index 6e6328b24..000000000 --- a/src/dotty/tools/dotc/util/lrutest.sc +++ /dev/null @@ -1,40 +0,0 @@ -package dotty.tools.dotc.util - -object lrutest { - println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet - val bits = new SixteenNibbles(0L) //> bits : dotty.tools.dotc.util.SixteenNibbles = SixteenNibbles(0, 0, 0, 0, 0, - //| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - bits.updated(1, 3) //> res0: dotty.tools.dotc.util.SixteenNibbles = SixteenNibbles(0, 3, 0, 0, 0, 0 - //| , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - LRUCache.initialRing //> res1: dotty.tools.dotc.util.SixteenNibbles = SixteenNibbles(1, 2, 3, 4, 5, 6 - //| , 7, 0, 0, 0, 0, 0, 0, 0, 0, 0) - val cache = new LRUCache[String, String] //> cache : dotty.tools.dotc.util.LRUCache[String,String] = LRUCache() - cache lookup "hi" //> res2: String = null - cache enter ("hi", "x") - cache.indices.take(10).toList //> res3: List[Int] = List(7, 0, 1, 2, 3, 4, 5, 6, 7, 0) - cache.last //> res4: Int = 6 - cache lookup "hi" //> res5: String = x - cache.indices.take(10).toList //> res6: List[Int] = List(7, 0, 1, 2, 3, 4, 5, 6, 7, 0) - - for (i <- 1 to 10) { - if (cache.lookup(i.toString) == null) - cache.enter(i.toString, i.toString) - } - - cache.indices.take(10).toList //> res7: List[Int] = List(5, 6, 7, 0, 1, 2, 3, 4, 5, 6) - cache //> res8: dotty.tools.dotc.util.LRUCache[String,String] = LRUCache(10 -> 10, 9 - - //| > 9, 8 -> 8, 7 -> 7, 6 -> 6, 5 -> 5, 4 -> 4, 3 -> 3) - cache //> res9: dotty.tools.dotc.util.LRUCache[String,String] = LRUCache(10 -> 10, 9 - - //| > 9, 8 -> 8, 7 -> 7, 6 -> 6, 5 -> 5, 4 -> 4, 3 -> 3) - cache.lookup("7") //> res10: String = 7 - cache.indices.take(10).toList //> res11: List[Int] = List(0, 5, 6, 7, 1, 2, 3, 4, 0, 5) - cache.keysIterator.toList //> res12: List[String] = List(7, 10, 9, 8, 6, 5, 4, 3) - cache.lookup("10") //> res13: String = 10 - cache.lookup("5") //> res14: String = 5 - cache //> res15: dotty.tools.dotc.util.LRUCache[String,String] = LRUCache(5 -> 5, 10 - - //| > 10, 7 -> 7, 9 -> 9, 8 -> 8, 6 -> 6, 4 -> 4, 3 -> 3) - cache.lookup("11") //> res16: String = null - cache.enter("11", "!!") - cache //> res17: dotty.tools.dotc.util.LRUCache[String,String] = LRUCache(11 -> !!, 5 - //| -> 5, 10 -> 10, 7 -> 7, 9 -> 9, 8 -> 8, 6 -> 6, 4 -> 4) -}
\ No newline at end of file |