diff options
author | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2005-12-19 13:49:03 +0000 |
---|---|---|
committer | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2005-12-19 13:49:03 +0000 |
commit | ac849228490d5a0e2d3f048d649297d5c59b6ade (patch) | |
tree | 6314f2c06f37e67dec5827c3f94e25cf844a085c /src/compiler/scala/tools/nsc | |
parent | d6c0efe5b4b89a0337f1cdcdabf8c607d81f4ae1 (diff) | |
download | scala-ac849228490d5a0e2d3f048d649297d5c59b6ade.tar.gz scala-ac849228490d5a0e2d3f048d649297d5c59b6ade.tar.bz2 scala-ac849228490d5a0e2d3f048d649297d5c59b6ade.zip |
Switching to the new build system and to the ne...
Switching to the new build system and to the new build system. This is a
MAJOR commit, so be careful when updating.
Diffstat (limited to 'src/compiler/scala/tools/nsc')
125 files changed, 36692 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala new file mode 100644 index 0000000000..0c39d99c16 --- /dev/null +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -0,0 +1,37 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +import scala.tools.nsc.util.{SourceFile, Position}; +import scala.tools.nsc.util.FreshNameCreator; +import scala.tools.util.AbstractFile; +import scala.collection.mutable.HashSet; + +[_trait_] abstract class CompilationUnits: Global { + + class CompilationUnit(val source: SourceFile, val mixinOnly: boolean) { + + /** short constructor */ + def this(source: SourceFile) = this(source, false); + + /** the fresh name creator */ + val fresh = new FreshNameCreator; + + /** the content of the compilation unit in tree form */ + var body: Tree = EmptyTree; + + val depends = new HashSet[AbstractFile]; + + def position(pos: int) = new Position(source, pos); + + def error(pos: int, msg: String) = reporter.error(position(pos), msg); + def warning(pos: int, msg: String) = reporter.warning(position(pos), msg); + override def toString() = source.toString(); + + } +} + + diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala new file mode 100644 index 0000000000..9768eaa621 --- /dev/null +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -0,0 +1,66 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +import scala.tools.util.Reporter; + +/** A class representing command line info for scalac */ +class CompilerCommand(arguments: List[String], error: String => unit, interactive: boolean) { + private var fs: List[String] = List(); + + /** All files to compile */ + def files: List[String] = fs.reverse; + + /** The applicable settings */ + val settings: Settings = new Settings(error); + + /** A message explaining usage and options */ + def usageMsg: String = { + val helpSyntaxColumnWidth: int = + Iterable.max(settings.allSettings map (. helpSyntax.length())); + def format(s: String): String = { + val buf = new StringBuffer(); + buf.append(s); + var i = s.length(); + while (i < helpSyntaxColumnWidth) { buf.append(' '); i = i + 1 } + buf.toString() + } + settings.allSettings + .map(setting => + format(setting.helpSyntax) + " " + setting.helpDescription) + .mkString( + "Usage: scalac <options | source files>\n" + + "where possible options include: \n ", + "\n ", + "\n"); + } + + // initialization + var args = arguments; + var ok = true; + while (!args.isEmpty && ok) { + val args0 = args; + if (args.head.startsWith("-")) { + if (interactive) { + error("no options can be given in interactive mode"); + ok = false + } else { + for (val setting <- settings.allSettings) + args = setting.tryToSet(args); + if (args eq args0) { + error("unknown option: '" + args.head + "'"); + ok = false + } + } + } else if (args.head.endsWith(".scala")) { + fs = args.head :: fs; + args = args.tail + } else { + error("don't know what to do with " + args.head); + ok = false + } + } +} diff --git a/src/compiler/scala/tools/nsc/CompilerRun.scala b/src/compiler/scala/tools/nsc/CompilerRun.scala new file mode 100644 index 0000000000..7677fc2cb8 --- /dev/null +++ b/src/compiler/scala/tools/nsc/CompilerRun.scala @@ -0,0 +1,20 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +class CompilerRun { + def firstPhase: Phase = NoPhase; + def terminalPhase: Phase = NoPhase; + def namerPhase: Phase = NoPhase; + def typerPhase: Phase = NoPhase; + def refchecksPhase: Phase = NoPhase; + def explicitOuterPhase: Phase = NoPhase; + def erasurePhase: Phase = NoPhase; + def flattenPhase: Phase = NoPhase; + def mixinPhase: Phase = NoPhase; + def phaseNamed(name: String): Phase = NoPhase; +} + diff --git a/src/compiler/scala/tools/nsc/EvalLoop.scala b/src/compiler/scala/tools/nsc/EvalLoop.scala new file mode 100644 index 0000000000..240547491a --- /dev/null +++ b/src/compiler/scala/tools/nsc/EvalLoop.scala @@ -0,0 +1,19 @@ +package scala.tools.nsc; + +import java.io._; + +trait EvalLoop { + + def prompt: String; + + def loop(action: (String) => Unit): unit = { + val in = new BufferedReader(new InputStreamReader(System.in)); + System.out.print(prompt); + var line = in.readLine(); + while (line != null && line.length() > 0) { + action(line); + System.out.print(prompt); + line = in.readLine(); + } + } +} diff --git a/src/compiler/scala/tools/nsc/FatalError.scala b/src/compiler/scala/tools/nsc/FatalError.scala new file mode 100644 index 0000000000..aa849fa5d0 --- /dev/null +++ b/src/compiler/scala/tools/nsc/FatalError.scala @@ -0,0 +1,8 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +case class FatalError(msg: String) extends Exception; diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala new file mode 100644 index 0000000000..75558c30b5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -0,0 +1,518 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +import java.io._; +import java.nio.charset._; +import scala.tools.util.{SourceReader,ClassPath,AbstractFile}; +import scala.tools.nsc.util.{Position,SourceFile}; +import scala.tools.nsc.reporters._; + +import scala.collection.mutable.{HashSet,HashMap} + +import symtab._; +import symtab.classfile.{PickleBuffer, Pickler}; +import util.{ListBuffer, Statistics}; +import ast._; +import ast.parser._; +import typechecker._; +import matching.TransMatcher; +import transform._; +import backend.icode.{ICodes, GenICode, Checkers}; +import backend.ScalaPrimitives; +import backend.jvm.GenJVM; + +class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable + with Trees + with CompilationUnits +{ + + // sub-components -------------------------------------------------- + + object treePrinters extends TreePrinters { + val global: Global.this.type = Global.this + } + val treePrinter = treePrinters.create(); + + object treeBrowsers extends TreeBrowsers { + val global: Global.this.type = Global.this + } + val treeBrowser = treeBrowsers.create(); + + object treeInfo extends TreeInfo { + val global: Global.this.type = Global.this + } + + object gen extends TreeGen { + val global: Global.this.type = Global.this + } + + object constfold extends ConstantFolder { + val global: Global.this.type = Global.this + } + + object checker extends TreeCheckers { + val global: Global.this.type = Global.this + } + + object icodes extends ICodes { + val global: Global.this.type = Global.this + } + + object checkers extends Checkers { + val global: Global.this.type = Global.this + } + + object statistics extends Statistics { + val global: Global.this.type = Global.this + } + + object overridingPairs extends OverridingPairs { + val global: Global.this.type = Global.this + } + + val copy = new LazyTreeCopier(); + +// reporting ------------------------------------------------------- + + def error(msg: String) = reporter.error(null, msg); + def warning(msg: String) = reporter.warning(null, msg); + def inform(msg: String) = System.err.println(msg); + + //reporter.info(null, msg, true); + + def informProgress(msg: String) = + if (settings.verbose.value) inform("[" + msg + "]"); + + def informTime(msg: String, start: long) = + informProgress(msg + " in " + (System.currentTimeMillis() - start) + "ms"); + + def log(msg: Object): unit = + if (settings.log contains phase.name) inform("[log " + phase + "] " + msg); + + def abort(msg: String) = throw new Error(msg); + +// file interface ------------------------------------------------------- + + private val reader: SourceReader = { + def stdCharset: Charset = { + settings.encoding.value = "ISO-8859-1"; // A mandatory charset + Charset.forName(settings.encoding.value); + } + val charset = + try { + Charset.forName(settings.encoding.value); + } catch { + case _: IllegalCharsetNameException => + error("illegal charset name '" + settings.encoding.value + "'"); + stdCharset + case _: UnsupportedCharsetException => + error("unsupported charset '" + settings.encoding.value + "'"); + stdCharset + } + new SourceReader(charset.newDecoder()); + } + + val classPath = new ClassPath( + settings.classpath.value, + settings.sourcepath.value, + settings.bootclasspath.value, + settings.extdirs.value); + + if (settings.verbose.value) { + System.err.println("classpath = " + classPath); + } + + def getSourceFile(f: AbstractFile): SourceFile = + new SourceFile(f, reader.read(f)); + + def getSourceFile(name: String): SourceFile = { + val f = AbstractFile.getFile(name); + if (f == null) throw new FileNotFoundException( + "source file '" + name + "' could not be found"); + getSourceFile(f) + } + + def getSourceFile(clazz: Symbol): SourceFile = { + val f = classPath.getRoot().lookupPath( + clazz.fullNameString(File.separatorChar) + ".scala", false); + if (f == null) throw new FileNotFoundException( + "source file for " + clazz + " could not be found"); + getSourceFile(f) + } + + object loaders extends SymbolLoaders { + val global: Global.this.type = Global.this + } + + def rootLoader: LazyType = new loaders.PackageLoader(classPath.getRoot()); + +// Phases ------------------------------------------------------------ + + var globalPhase: Phase = NoPhase; + + val MaxPhases = 64; + + val phaseWithId = new Array[Phase](MaxPhases); + { for (val i <- List.range(0, MaxPhases)) phaseWithId(i) = NoPhase } + + abstract class GlobalPhase(prev: Phase) extends Phase(prev) { + phaseWithId(id) = this; + def run: unit = currentRun.units foreach applyPhase; + + def apply(unit: CompilationUnit): unit; + private val isErased = prev.name == "erasure" || prev.erasedTypes; + override def erasedTypes: boolean = isErased; + private val isFlat = prev.name == "flatten" || prev.flatClasses; + override def flatClasses: boolean = isFlat; + final def applyPhase(unit: CompilationUnit): unit = { + if (settings.debug.value) inform("[running phase " + name + " on " + unit + "]"); + val unit0 = currentRun.currentUnit; + currentRun.currentUnit = unit; + apply(unit); + currentRun.advanceUnit; + assert(currentRun.currentUnit == unit); + currentRun.currentUnit = unit0; + } + } + + object syntaxAnalyzer extends SyntaxAnalyzer { + val global: Global.this.type = Global.this + } + + object analyzer extends Analyzer { + val global: Global.this.type = Global.this; + } + + object superAccessors extends SuperAccessors { + val global: Global.this.type = Global.this + } + + object pickler extends Pickler { + val global: Global.this.type = Global.this + } + + object refchecks extends RefChecks { + val global: Global.this.type = Global.this; + } + + object uncurry extends UnCurry { + val global: Global.this.type = Global.this; + } + + object tailCalls extends TailCalls { + val global: Global.this.type = Global.this; + } + + object transMatcher extends TransMatcher { + val global: Global.this.type = Global.this; + } + + object explicitOuter extends ExplicitOuter { + val global: Global.this.type = Global.this; + } + + object erasure extends Erasure { + val global: Global.this.type = Global.this; + } + + object lambdaLift extends LambdaLift { + val global: Global.this.type = Global.this; + } + + object constructors extends Constructors { + val global: Global.this.type = Global.this; + } + + object flatten extends Flatten { + val global: Global.this.type = Global.this; + } + + object mixin extends Mixin { + val global: Global.this.type = Global.this; + } + + object sampleTransform extends SampleTransform { + val global: Global.this.type = Global.this; + } + + object genicode extends GenICode { + val global: Global.this.type = Global.this; + } + + object icodePrinter extends backend.icode.Printers { + val global: Global.this.type = Global.this; + } + + object scalaPrimitives extends ScalaPrimitives { + val global: Global.this.type = Global.this; + } + + object genJVM extends GenJVM { + val global: Global.this.type = Global.this; + } + + object icodeChecker extends checkers.ICodeChecker(); + + object typer extends analyzer.Typer( + analyzer.NoContext.make(EmptyTree, Global.this.definitions.RootClass, new Scope())); + + def phaseDescriptors: List[SubComponent] = List( + analyzer.namerFactory, + analyzer.typerFactory, + superAccessors, + pickler, + refchecks, + uncurry, + tailCalls, + transMatcher, + explicitOuter, + erasure, + lambdaLift, + constructors, + flatten, + mixin, + genicode, + genJVM, + sampleTransform); + + private var curRun: Run = NoRun; + override def currentRun: Run = curRun; + + class TyperRun extends Run { + override val terminalPhase : Phase = typerPhase.next.next; + //override val terminalPhase : Phase = superAccessors.next; + } + + + + class Run extends CompilerRun { + var currentUnit : CompilationUnit = _; + curRun = this; + override val firstPhase = syntaxAnalyzer.newPhase(NoPhase); + phase = firstPhase; + definitions.init; // needs firstPhase and phase to be defined != NoPhase, + // that's why it is placed here. + icodes.init; + + private var p: Phase = firstPhase; + private var stopped = false; + for (val pd <- phaseDescriptors) { + if (!stopped) { + if (!(settings.skip contains pd.phaseName)) p = pd.newPhase(p); + stopped = settings.stop contains pd.phaseName; + } + } + + // progress tracking + def progress(current : Int, total : Int) : Unit = {} + private var phasec : Int = 0; + private var unitc : Int = 0; + def advancePhase : Unit = { + unitc = 0; + phasec = phasec + 1; + refreshProgress; + } + def advanceUnit : Unit = { + unitc = unitc + 1; + refreshProgress; + } + private def refreshProgress = if (fileset.size > 0) + progress((phasec * fileset.size) + unitc, + (phaseDescriptors.length+1) * fileset.size); + + + + override val terminalPhase : Phase = new GlobalPhase(p) { + def name = "terminal"; + def apply(unit: CompilationUnit): unit = {} + } + override def phaseNamed(name: String): Phase = { + var p: Phase = firstPhase; + while (p.next != p && p.name != name) p = p.next; + if (p.name != name) NoPhase else p + } + + override val namerPhase = phaseNamed("namer"); + override val typerPhase = phaseNamed("typer"); + override val refchecksPhase = phaseNamed("refchecks"); + override val explicitOuterPhase = phaseNamed("explicitouter"); + override val erasurePhase = phaseNamed("erasure"); + override val flattenPhase = phaseNamed("flatten"); + override val mixinPhase = phaseNamed("mixin"); + + private var unitbuf = new ListBuffer[CompilationUnit]; + private var fileset = new HashSet[AbstractFile]; + + private def addUnit(unit: CompilationUnit): unit = { + unitbuf += unit; + fileset += unit.source.getFile(); + } + + def units: Iterator[CompilationUnit] = unitbuf.elements; + + /** A map from compiled top-level symbols to their source files */ + val symSource = new HashMap[Symbol, AbstractFile]; + + /** A map from compiled top-level symbols to their picklers */ + val symData = new HashMap[Symbol, PickleBuffer]; + + def compileSources(sources: List[SourceFile]): unit = { + val startTime = System.currentTimeMillis(); + reporter.reset; + for (val source <- sources) + addUnit(new CompilationUnit(source)); + + globalPhase = firstPhase; + while (globalPhase != terminalPhase && reporter.errors == 0) { + val startTime = System.currentTimeMillis(); + phase = globalPhase; + globalPhase.run; + if (settings.print contains globalPhase.name) treePrinter.printAll(); + if (settings.browse contains globalPhase.name) treeBrowser.browse(units); + informTime(globalPhase.description, startTime); + globalPhase = globalPhase.next; + if (settings.check contains globalPhase.name) { + phase = globalPhase; + if (globalPhase.name == "jvm") icodeChecker.checkICodes; + else checker.checkTrees; + } + if (settings.statistics.value) statistics.print(phase); + advancePhase; + } + + if (settings.Xshowcls.value != "") showDef(newTermName(settings.Xshowcls.value), false); + if (settings.Xshowobj.value != "") showDef(newTermName(settings.Xshowobj.value), true); + if (settings.Xshowicode.value) writeICode(); + + if (reporter.errors == 0) { + assert(symData.isEmpty, symData.elements.toList); +/* + for (val Pair(sym, pickled) <- symData.elements.toList) { + sym setPos Position.NOPOS; +// if (symData contains sym) { +// symData -= sym; +// symData -= sym.linkedSym; +// writeSymblFile(sym, pickled) +// } +// } +*/ + for (val Pair(sym, file) <- symSource.elements) { + sym setPos Position.NOPOS; + if (sym.isTerm) sym.moduleClass setPos Position.NOPOS; + resetPackageClass(sym.owner); + } + } else { + for (val Pair(sym, file) <- symSource.elements) { + sym.reset(new loaders.SourcefileLoader(file)); + if (sym.isTerm) sym.moduleClass.reset(loaders.moduleClassLoader); + } + } + informTime("total", startTime); + } + + def compileLate(file: AbstractFile): unit = + if (fileset == null) + throw new FatalError("No class file for " + file + " was found\n(This file cannot be loaded as a source file)"); + else if (!(fileset contains file)) { + val unit = new CompilationUnit(getSourceFile(file)); + addUnit(unit); + var localPhase = firstPhase.asInstanceOf[GlobalPhase]; + while (localPhase.id < globalPhase.id || localPhase.id <= namerPhase.id) { + atPhase(localPhase)(localPhase.applyPhase(unit)); + localPhase = localPhase.next.asInstanceOf[GlobalPhase]; + } + refreshProgress; + } + + def compileFiles(files: List[AbstractFile]): unit = + try { + compileSources(files map getSourceFile) + } catch { + case ex: IOException => error(ex.getMessage()); + } + + def compile(filenames: List[String]): unit = + try { + compileSources(filenames map getSourceFile) + } catch { + case ex: IOException => error(ex.getMessage()); + } + + private def resetPackageClass(pclazz: Symbol): unit = { + assert(pclazz.isPackageClass, pclazz); + atPhase(firstPhase) { + pclazz.setInfo(atPhase(typerPhase)(pclazz.info)) + } + if (!pclazz.isRoot) resetPackageClass(pclazz.owner); + } + } + + def showDef(name: Name, module: boolean): unit = { + def getSym(name: Name, module: boolean): Symbol = { + var i = name.length - 1; + while (i != 0 && name(i) != '#' && name(i) != '.') i = i - 1; + if (i == 0) + definitions.getModule(name) + else { + val root = getSym(name.subName(0, i), name(i) == '.'); + var selector = name.subName(i+1, name.length); + if (module) selector = selector.toTypeName; + root.info.member(selector) + } + } + val sym = getSym(name, module); + System.err.println("" + sym.name + ":" + + (if (module) sym.tpe.symbol.info else sym.info)) + } + + /** Returns the file with the given suffix for the given class. */ + def getFile(clazz: Symbol, suffix: String) = { + val outdirname = settings.outdir.value; + var outdir = new File(if (outdirname == "") "." else outdirname); + val filename = clazz.fullNameString('.'); + var start = 0; + var end = filename.indexOf('.', start); + while (end >= start) { + outdir = new File(outdir, filename.substring(start, end)); + if (!outdir.exists()) outdir.mkdir(); + start = end + 1; + end = filename.indexOf('.', start); + } + new File(outdir, filename.substring(start) + suffix) + } + + private def writeSymblFile(clazz: Symbol, pickled: PickleBuffer) = { + val file = getFile(clazz, ".symbl"); + try { + val stream = new FileOutputStream(file); + stream.write(pickled.bytes, 0, pickled.writeIndex); + stream.close(); + informProgress("wrote " + file); + } catch { + case ex: IOException => + if (settings.debug.value) ex.printStackTrace(); + error("could not write file " + file); + } + } + + private def writeICode(): Unit = { + val printer = new icodePrinter.TextPrinter(null); + icodes.classes.foreach((cls) => { + val file = getFile(cls.symbol, ".icode"); + try { + val stream = new FileOutputStream(file); + printer.setWriter(new PrintWriter(stream, true)); + printer.printClass(cls); + informProgress("wrote " + file); + } catch { + case ex: IOException => + if (settings.debug.value) ex.printStackTrace(); + error("could not write file " + file); + } + }); + } +} diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala new file mode 100644 index 0000000000..847cdade6a --- /dev/null +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -0,0 +1,234 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +import scala.tools.nsc.reporters.Reporter; + +abstract class Interpreter { + import scala.collection.mutable.ListBuffer; + import symtab.Names; + + // list of names defined, for each line number + val prevDefines : ListBuffer[Pair[Int,ListBuffer[Names#Name]]] = new ListBuffer(); + + val compiler: Global; + + import scala.tools.nsc.ast.parser.SyntaxAnalyzer; + object syntaxAnalyzer extends SyntaxAnalyzer { + val global: compiler.type = compiler + } + + def interpret(line: String, reporter: Reporter): unit = { + import scala.tools.nsc.util.SourceFile; + + // convert input to a compilation unit, using SourceFile; + // and parse it, using syntaxAnalyzer, to get input ASTs + val inASTs = syntaxAnalyzer.interpreterParse( + new compiler.CompilationUnit( + //if SourceFile is not modified, then fix a bug + //here by adding an EOF character to the end of + //the 'line' + new SourceFile("<console>",line.toCharArray()))); + + //todo: if (errors in parsing) after reporting them, exit method + + val dvt = new DefinedVarsTraverser; + dvt.traverseTrees(inASTs); + val definedVars = dvt.definedVars; + + val ivt = new ImportVarsTraverser(definedVars); + ivt.traverseTrees(inASTs); + val importVars = ivt.importVars; + + val lineno = prevDefines.length; + //todo: it is probably nice to include date & time, as well as a process id, in the filename + val filename = getTempPath().getPath()+java.io.File.separator+"InterpreterTempLine"+lineno+".scala"; + writeTempScalaFile(filename, line, lineno, definedVars, importVars); + + // first phase: compile auto-generated file + compiler.settings.outdir.value = getTempPath().getPath(); + val cr = new compiler.Run; + cr compile List(filename); + + //todo: if no errors in compilation then + // second phase: execute JVM, and print outcome + // else consider definition as if has not happened and exit method + //todo: use Scala's reflection API, which I designed, instead, for the following code + val cl = new java.net.URLClassLoader(Predef.Array(getTempPath().toURL())); + val interpreterResultObject: Class = Class.forName("InterpreterLine"+lineno+"Result",true,cl); + val resultValMethod: java.lang.reflect.Method = interpreterResultObject.getMethod("result",null); + var interpreterResultString: String = resultValMethod.invoke(interpreterResultObject,null).toString(); + + //var interpreterResultJavaTypeString: String = resultValMethod.getReturnType().getName(); + //Console.println(compiler.definitions.EmptyPackage.info.members); + val interpreterResultSym: compiler.Symbol = + compiler.definitions.getMember(compiler.definitions.EmptyPackage, + compiler.newTermName("InterpreterLine"+lineno+"Result")); + + def findSymbolWithName(ls: List[compiler.Symbol], name: compiler.Name): compiler.Symbol = + ls.find(s=>s.name == name) match { + case None => throw new IllegalStateException("Cannot find field '"+name+"' in InterpreterResult"); + case Some(s) => s; + } + + //var lastname: String = compiler.atPhase(cr.typerPhase.next){interpreterResultSym.info.decls.toList.last.name.toString()}; + //reporter.info(null,lastname,true); + //todo: similar to what I should be doing for Scala's reflection?? + var interpreterResultScalaTypeString: String = + compiler.atPhase(cr.typerPhase.next){ + findSymbolWithName(interpreterResultSym.info.decls.toList, + compiler.nme.getterToLocal(compiler.newTermName("result"))) + .tpe.toString() + }; + reporter.info(null,interpreterResultString+": "+interpreterResultScalaTypeString/*+" ("+interpreterResultJavaTypeString+")"*/,true); + +/* + val scalaInterpFile: File = ScalaInterpFile(filename); + scalaInterpFile.deleteOnExit(); + if(scalaInterpFile.exists()) + scalaInterpFile.delete(); + + getvalue of line#.last_var_defined_in_line (from defined_vars) + (works for 'it' as it was added as last val to definedvars) + and send it to reporter +*/ + + // book-keeping + //todo: if no errors in evaluation then + prevDefines += Pair(lineno,definedVars); + // else consider definition as if has not happened. + + // report some debug info + //reporter.info(null,"inASTs="+inASTs,true); + //reporter.info(null,"definedVars="+definedVars,true); + //reporter.info(null,"importVars="+importVars,true); + //reporter.info(null,"prevDefines="+prevDefines,true); + } + + import java.io.File; + def getTempPath(): File = { + val tempdir = { + val tempdir1 = System.getProperty("java.io.tmpdir"); + if (tempdir1 == null){ + val tempdir2 = System.getProperty("TEMP"); + if (tempdir2 == null){ + val tempdir3 = System.getProperty("TMP"); + if (tempdir3 == null) + throw new IllegalStateException("No temporary folder defined") + else tempdir3 } + else tempdir2 } + else tempdir1 + }; + val path = new File(tempdir); + if (!path.exists() || !path.isDirectory()) + throw new IllegalStateException("Invalid temporary directory") + else if (!path.canWrite()) + throw new IllegalStateException("Temporary directory not writable") + else path + }; + + def writeTempScalaFile(filename: String, line: String, lineno: Int, definedVars: ListBuffer[Names#Name], importVars: ListBuffer[Pair[Names#Name,Int]]) = { + import java.io.{File, PrintWriter, FileOutputStream}; + val scalaFile = new File(filename); + scalaFile.deleteOnExit(); + if(scalaFile.exists()) // to prevent old lingering files from having results from them reported! + scalaFile.delete(); + + val module = new PrintWriter(new FileOutputStream(scalaFile)); + //todo:"import "+LoadedModules?.getName + //module.println("\n"); + + for(val Pair(ivname,ivlineno) <- importVars.toList) yield + module.println("import line"+ivlineno+"."+ivname+";\n"); + + module.println("object line"+lineno+" {"); + var fullLine = line; + if(definedVars.length == 0) { // input is just an expression + fullLine = " var it = " + line; + definedVars += compiler.encode("it"); } + else fullLine = " " + line; + module.println(fullLine); + module.println("}"); + module.println(); + module.println("object InterpreterLine"+lineno+"Result "); + module.println("{ val result = (line"+lineno+"."+definedVars.toList.reverse.head+"); }"); + // reflection is used later (see above) to get the result value above + + module.flush(); + module.close(); + } + + import compiler.Traverser; + import compiler.Tree; + class DefinedVarsTraverser extends Traverser { + val definedVars = new ListBuffer[Names#Name]; + override def traverse(ast: Tree): unit = + if (!ast.isDef) () + else { + import compiler._; + ast match { + // only the outer level needed, so do not recurse to go deeper + // todo: combine similar cases in one case + case ClassDef(_,name,_,_,_) => definedVars += name + case ModuleDef(_, name,_) => definedVars += name + case ValDef(_, name,_,_) => definedVars += name + case DefDef(_,name,_,_,_,_) => definedVars += name + //todo:case Bind(name,_) => ((name != nme.WILDCARD) && (definedVars.elements forall (name !=))) definedVars += name + + //case Ident(name) => if (name...is defined) definedVars += name; + + //todo: + //case PackageDef(name, _) => throw new InterpIllegalDefException(name.toString()+": package definitions not allowed") + //case AbsTypeDef(_,name,_,_) => throw new InterpIllegalDefException(name.toString()+": absract type definitions not allowed") + //case AliasTypeDef(_,name,_,_) => throw new InterpIllegalDefException(name.toString()+": alias type definitions not allowed") + //case LabelDef(name,_,_) => throw new InterpIllegalDefException(name.toString()+": label definitions not allowed") + case _ => throw new InterpIllegalDefException("Unsupported interpreter definition. Contact Scala developers for adding interpreter support for it.")// () + } + } + } + case class InterpIllegalDefException(msg: String) extends RuntimeException(msg); + +// class ListTraverser extends Traverser { +// def traverse(trees: List[Tree]): Unit = +// trees foreach traverse; +// } +// +// class ListDefinedVarsTraverser extends DefinedVarsTraverser with ListTraverser; + + class ImportVarsTraverser(definedVars: ListBuffer[Names#Name]) extends Traverser { + val importVars = new ListBuffer[Pair[Names#Name,Int]]; + var curAST = 0; + import compiler.Ident; + override def traverse(ast: Tree): unit = ast match { + case Ident(name) => { + var lastPrevDefsIdx = -1; + //reporter.info(null,"name="+name,true); + for(val Pair(lineno,defs) <- prevDefines.toList) yield { + //reporter.info(null,"line#="+lineno+", defs="+defs,true); + if (defs.indexOf(name) != -1) lastPrevDefsIdx = lineno + } + val foundInPrevDefines = (lastPrevDefsIdx != -1); + //reporter.info(null,"lastPrevDefsIdx="+lastPrevDefsIdx,true); + if(foundInPrevDefines) { + val firstCurDefIdx = definedVars.indexOf(name); + val foundInDefinedVars = (firstCurDefIdx != -1); + if((!foundInDefinedVars || + (foundInDefinedVars && (firstCurDefIdx > curAST))) + && (importVars.indexOf(Pair(name,lastPrevDefsIdx)) == -1)) + // to prevent duplicate imports (todo: use find instead of indexOf?) + importVars += Pair(name,lastPrevDefsIdx); + } + } + case _ => { + // using case x, instead of case _, we can have: reporter.info(null,"x="+x,true); + super.traverse(ast) + } + } + override def traverseTrees(asts: List[Tree]): unit = + asts foreach { curAST = curAST+1; traverse; } + } + //todo: unit-test cases +} diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala new file mode 100644 index 0000000000..58b352db61 --- /dev/null +++ b/src/compiler/scala/tools/nsc/Main.scala @@ -0,0 +1,71 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +import scala.tools.nsc.util.{Position}; +import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}; + +/** The main class for NSC, a compiler for the programming + * language Scala. + */ +object Main extends Object with EvalLoop { + + val PRODUCT: String = + System.getProperty("scala.product", "scalac"); + val VERSION: String = + System.getProperty("scala.version", "unknown version"); + val versionMsg = PRODUCT + " " + VERSION + " -- (c) 2002-05 LAMP/EPFL"; + val prompt = "\nnsc> "; + + private var reporter: ConsoleReporter = _; + + def error(msg: String): unit = + reporter.error(new Position(PRODUCT), + msg + "\n " + PRODUCT + " -help gives more information"); + + def errors() = reporter.errors; + + def resident(compiler: Global): unit = { + loop(line => { + val args = List.fromString(line, ' '); + val command = new CompilerCommand(args, error, true); + (new compiler.Run) compile command.files + }) + } + + def process(args: Array[String]): unit = { + reporter = new ConsoleReporter(); + val command = new CompilerCommand(List.fromArray(args), error, false); + reporter.prompt = command.settings.prompt.value; + if (command.settings.version.value) + reporter.info(null, versionMsg, true) + else if (command.settings.help.value) + reporter.info(null, command.usageMsg, true) + else { + try { + val compiler = new Global(command.settings, reporter); + if (command.settings.resident.value) + resident(compiler); + else if (command.files.isEmpty) + reporter.info(null, command.usageMsg, true) + else + (new compiler.Run) compile command.files; + } catch { + case ex @ FatalError(msg) => + if (command.settings.debug.value) + ex.printStackTrace(); + reporter.error(null, "fatal error: " + msg); + } + reporter.printSummary() + } + } + + def main(args: Array[String]): unit = { + process(args); + System.exit(if (reporter.errors > 0) 1 else 0); + } + +} diff --git a/src/compiler/scala/tools/nsc/MainInterpreter.scala b/src/compiler/scala/tools/nsc/MainInterpreter.scala new file mode 100644 index 0000000000..b1ea38e065 --- /dev/null +++ b/src/compiler/scala/tools/nsc/MainInterpreter.scala @@ -0,0 +1,74 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author emir + */ +// $Id$ +package scala.tools.nsc; + +import java.io._; +import scala.tools.nsc.util.{Position}; +import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}; + +/** The main class for the new scala interpreter. + */ +object MainInterpreter extends Object with EvalLoop { + // lots of stuff duplicated from Main + val PRODUCT: String = + System.getProperty("scala.product", "scalaint"); + val VERSION: String = + System.getProperty("scala.version", "unknown version"); + val versionMsg = PRODUCT + " " + VERSION + " -- (c) 2002-05 LAMP/EPFL"; + val prompt = "\nnsc> "; + + private var reporter: ConsoleReporter = _; + + def error(msg: String): unit = + reporter.error(new Position(PRODUCT), + msg + "\n " + PRODUCT + " -help gives more information"); + + def errors() = reporter.errors; + + def interpret(gCompiler: Global): unit = { + val interpreter = new Interpreter { + val compiler: gCompiler.type = gCompiler + }; + loop(line => try { + interpreter.interpret(line.trim(), reporter) + } catch { + case e: Exception => { + reporter.info(null,e.getMessage(),true); + //e.printStackTrace(); + } + } + ) + } + + def process(args: Array[String]): unit = { + reporter = new ConsoleReporter(); + val command = new CompilerCommand(List.fromArray(args), error, false); + reporter.prompt = (command.settings.prompt.value); + if (command.settings.version.value) + reporter.info(null, versionMsg, true) + else if (command.settings.help.value) // 2do replace with InterpCommand + reporter.info(null, command.usageMsg, true) + + else { + try { + val compiler = new Global(command.settings, reporter); + interpret(compiler); + } catch { + case ex @ FatalError(msg) => + if (command.settings.debug.value) + ex.printStackTrace(); + reporter.error(null, "fatal error: " + msg); + } + reporter.printSummary() + } + } + + def main(args: Array[String]): unit = { + process(args); + System.exit(if (reporter.errors > 0) 1 else 0); + } + +} diff --git a/src/compiler/scala/tools/nsc/MainTokenMetric.scala b/src/compiler/scala/tools/nsc/MainTokenMetric.scala new file mode 100644 index 0000000000..df05b627e0 --- /dev/null +++ b/src/compiler/scala/tools/nsc/MainTokenMetric.scala @@ -0,0 +1,61 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +import java.io._; +import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}; + +/** The main class for NSC, a compiler for the programming + * language Scala. + */ +object MainTokenMetric { + + private var reporter: ConsoleReporter = _; + + def tokenMetric(compiler: Global, fnames: List[String]): unit = { + import compiler.CompilationUnit; + import compiler.syntaxAnalyzer.Scanner; + import ast.parser.Tokens.EOF; + var totale = 0; + for (val source <- fnames) { + val s = new Scanner(new CompilationUnit(compiler.getSourceFile(source))); + var i = 0; + while(s.token != EOF) { + i = i + 1; + s.nextToken() + } + var j = 0 ; while(j + Math.log(i)/ Math.log(10) < 7) { + j = j+1; + Console.print(' '); + } + Console.print(i.toString()); + Console.print(" "); + Console.println(source.toString()); + totale = totale + i; + } + Console.println(totale.toString()+" total"); + } + + def process(args: Array[String]): unit = { + val command = new CompilerCommand(List.fromArray(args), error, false); + reporter = new ConsoleReporter(); + try { + val compiler = new Global(command.settings, reporter); + tokenMetric(compiler, command.files); + } catch { + case ex @ FatalError(msg) => + if (command.settings.debug.value) + ex.printStackTrace(); + reporter.error(null, "fatal error: " + msg); + } + } + + def main(args: Array[String]): unit = { + process(args); + System.exit(if (reporter.errors > 0) 1 else 0); + } + +} diff --git a/src/compiler/scala/tools/nsc/NoPhase.scala b/src/compiler/scala/tools/nsc/NoPhase.scala new file mode 100644 index 0000000000..342b022842 --- /dev/null +++ b/src/compiler/scala/tools/nsc/NoPhase.scala @@ -0,0 +1,11 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +object NoPhase extends Phase(null) { + def name = "<no phase>"; + def run: unit = throw new Error("NoPhase.run"); +} diff --git a/src/compiler/scala/tools/nsc/Phase.scala b/src/compiler/scala/tools/nsc/Phase.scala new file mode 100644 index 0000000000..4ca3aa2e9c --- /dev/null +++ b/src/compiler/scala/tools/nsc/Phase.scala @@ -0,0 +1,35 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +import symtab.Flags; + +abstract class Phase(val prev: Phase) { + + type Id = int; + + val id: Id = if (prev == null) 0 else prev.id + 1; + + def newFlags: long = 0l; + private var fmask: long = + if (prev == null) Flags.InitialFlags else prev.flagMask | newFlags; + def flagMask: long = fmask; + + private var nx: Phase = this; + if (prev != null) prev.nx = this; + + def next: Phase = nx; + + def name: String; + def description: String = name; + def erasedTypes: boolean = false; + def flatClasses: boolean = false; + def run: unit; + + override def toString() = name; +} + + diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala new file mode 100644 index 0000000000..a7a5dfe73c --- /dev/null +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -0,0 +1,166 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +class Settings(error: String => unit) { + + private var allsettings: List[Setting] = List(); + + val debuginfo = BooleanSetting("-g", "Generate debugging info"); + val nowarnings = BooleanSetting("-nowarn", "Generate no warnings"); + val noassertions = BooleanSetting("-noassert", "Generate no assertions and assumptions"); + val verbose = BooleanSetting("-verbose", "Output messages about what the compiler is doing"); + val classpath = StringSetting ("-classpath", "path", "Specify where to find user class files", + System.getProperty("scala.class.path", + System.getProperty("java.class.path", "."))); + val sourcepath = StringSetting ("-sourcepath", "path", "Specify where to find input source files", + System.getProperty("scala.source.path", + System.getProperty("java.source.path", "."))); + val bootclasspath = StringSetting ("-bootclasspath", "path", "Override location of bootstrap class files", + System.getProperty("sun.boot.class.path", "")); + val extdirs = StringSetting ("-extdirs", "dirs", "Override location of installed extensions", + System.getProperty("java.ext.dirs", "")); + val outdir = StringSetting ("-d", "directory", "Specify where to place generated class files", ""); + val encoding = StringSetting ("-encoding", "encoding", "Specify character encoding used by source files", "ISO-8859-1"); + val separate = ChoiceSetting ("-separate", "Read symbol files for separate compilation", List("yes","no"), "default"); + val target = ChoiceSetting ("-target", "Specify which backend to use", List("jvm", "msil"), "jvm"); + val debug = BooleanSetting("-debug", "Output debugging messages"); + val statistics = BooleanSetting("-statistics", "Print compiler statistics"); + val explaintypes = BooleanSetting("-explaintypes", "Explain type errors in more detail"); + val resident = BooleanSetting("-resident", "Compiler stays resident, files to compile are read from standard input"); + val uniqid = BooleanSetting("-uniqid", "Print identifiers with unique names (debugging option)"); + val printtypes = BooleanSetting("-printtypes", "Print tree types (debugging option)"); + val prompt = BooleanSetting("-prompt", "Display a prompt after each error (debugging option)"); + val noimports = BooleanSetting("-noimports", "Compile without any implicit imports"); + val nopredefs = BooleanSetting("-nopredefs", "Compile without any implicit predefined values"); + val skip = PhasesSetting ("-skip", "Skip"); + val check = PhasesSetting ("-check", "Check the tree at start of"); + val print = PhasesSetting ("-print", "Print out program after"); + val printer = ChoiceSetting ("-printer", "Printer to use", List("text", "html"), "text"); + val printfile = StringSetting ("-printfile", "file", "Specify file in which to print trees", "-"); + val graph = PhasesSetting ("-graph", "Graph the program after"); + val browse = PhasesSetting ("-browse", "Browse the abstract syntax tree after"); + val stop = PhasesSetting ("-stop", "Stop after phase"); + val log = PhasesSetting ("-log", "Log operations in"); + val version = BooleanSetting("-version", "Print product version and exit"); + val help = BooleanSetting("-help", "Print a synopsis of standard options"); + + val Xshowcls = StringSetting ("-Xshowcls", "class", "Show class info", ""); + val Xshowobj = StringSetting ("-Xshowobj", "object", "Show object info", ""); + val Xshowicode = BooleanSetting("-Xshowicode", "Print the generated ICode"); + val Xgadt = BooleanSetting("-Xgadt", "enable gadt for classes"); + + /** A list of all settings */ + def allSettings: List[Setting] = allsettings.reverse; + + /** A base class for settings of all types. + * Subclasses each define a `value' field of the appropriate type. + */ + abstract class Setting(name: String, descr: String) { + + /** If first arg defines this setting, consume it as well as all following + * args needed to define the setting. If this can be done without + * error, set value field and return suffix of args else + * issue error message and return empty. + * If first arg does not define this setting return args unchanged. + */ + def tryToSet(args: List[String]): List[String]; + + /** The syntax defining this setting in a help string */ + def helpSyntax: String = name; + + /** A description of the purpose of this setting in a help string */ + def helpDescription = descr; + + // initialization + allsettings = this :: allsettings; + } + + /** A setting represented by a boolean flag (false, unless set) */ + case class BooleanSetting(name: String, descr: String) + extends Setting(name, descr) { + var value: boolean = false; + + def tryToSet(args: List[String]): List[String] = args match { + case n :: rest if (n == name) => value = true; rest + case _ => args + } + } + + /** A setting represented by a string, (`default' unless set) */ + case class StringSetting(name: String, arg: String, descr: String, default: String) + extends Setting(name, descr) { + var value: String = default; + + def tryToSet(args: List[String]): List[String] = args match { + case n :: rest if (n == name) => + if (rest.isEmpty) { + error("missing argument"); + List() + } else { + value = rest.head; + rest.tail + } + case _ => args + } + + override def helpSyntax = name + " <" + arg + ">"; + } + + /** A setting represented by a string in a given set of `choices', + * (`default' unless set) + */ + case class ChoiceSetting(name: String, descr: String, choices: List[String], default: String) + extends Setting(name, descr + choices.mkString(" (", ",", ")")) { + var value: String = default; + + private def argument: String = name.substring(1); + + def tryToSet(args: List[String]): List[String] = args match { + case n :: rest if (n startsWith (name + ":")) => + val choice = n.substring(name.length() + 1); + if (!(choices contains choice)) { + error( + if (choice == "") "missing " + argument + else "unknown " + argument + " '" + choice + "'"); + List() + } else { + value = choice; + rest + } + case _ => args + } + + override def helpSyntax = name + ":<" + argument + ">"; + } + + /** A setting represented by a list of strings which should be prefixes of + * phase names. This is not checked here, however. + * (the empty list, unless set) + */ + case class PhasesSetting(name: String, descr: String) + extends Setting(name, descr + " <phases> (see below)") { + var value: List[String] = List(); + + def tryToSet(args: List[String]): List[String] = args match { + case n :: rest if (n startsWith (name + ":")) => + val phase = n.substring(name.length() + 1); + if (phase == "") { + error("missing phase"); + List() + } else { + value = value ::: List(phase); + rest + } + case _ => args + } + + override def helpSyntax = name + ":<phase>"; + + def contains(phasename: String): boolean = + value exists (str => phasename startsWith str) + } +} diff --git a/src/compiler/scala/tools/nsc/SubComponent.scala b/src/compiler/scala/tools/nsc/SubComponent.scala new file mode 100644 index 0000000000..556b6538ad --- /dev/null +++ b/src/compiler/scala/tools/nsc/SubComponent.scala @@ -0,0 +1,29 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc; + +/** An nsc sub-component. + */ +abstract class SubComponent { + + /** The global environment; overridden by instantiation in Global. */ + val global: Global; + + /** The name of the phase */ + val phaseName: String; + + /** New flags defined by the phase which are not valid before */ + def phaseNewFlags: long = 0; + + /** The phase factory */ + def newPhase(prev: Phase): Phase; + + /** A standard phase template */ + abstract class StdPhase(prev: Phase) extends global.GlobalPhase(prev) { + def name = phaseName; + override def newFlags = phaseNewFlags; + } +} diff --git a/src/compiler/scala/tools/nsc/ant/NSC.scala b/src/compiler/scala/tools/nsc/ant/NSC.scala new file mode 100644 index 0000000000..9010a92bf9 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ant/NSC.scala @@ -0,0 +1,645 @@ +/* __ ______________ *\ +** / |/ / ____/ ____/ ** +** / | | /___ / /___ ** +** /_/|__/_____/_____/ Copyright 2005-2006 LAMP/EPFL ** +\* */ + +// $Id$ + + +package scala.tools.nsc.ant { + + import java.io.File; + import java.net.URL; + import java.net.URLClassLoader; + import java.util.ArrayList; + import java.util.Vector; + + import org.apache.tools.ant.AntClassLoader; + import org.apache.tools.ant.BuildException; + import org.apache.tools.ant.DirectoryScanner; + import org.apache.tools.ant.Project; + import org.apache.tools.ant.taskdefs.MatchingTask; + import org.apache.tools.ant.types.Path; + import org.apache.tools.ant.util.FileUtils; + import org.apache.tools.ant.util.GlobPatternMapper; + import org.apache.tools.ant.util.SourceFileScanner; + import org.apache.tools.ant.types.EnumeratedAttribute; + import org.apache.tools.ant.types.Reference; + + import scala.tools.nsc.reporters.{Reporter,ConsoleReporter}; + + /** + * An Ant task to compile with the new Scala compiler (NSC). + * This task can take the following parameters as attributes:<ul> + * <li>srcdir (mandatory),</li> + * <li>srcref,</li> + * <li>destdir,</li> + * <li>classpath,</li> + * <li>classpathref,</li> + * <li>sourcepath,</li> + * <li>sourcepathref,</li> + * <li>bootclasspath,</li> + * <li>bootclasspathref,</li> + * <li>extdirs,</li> + * <li>extdirsref,</li> + * <li>encoding,</li> + * <li>verbose,</li> + * <li>debug,</li> + * <li>usepredefs,</li> + * <li>useimports,</li> + * <li>force,</li> + * <li>stop,</li> + * <li>skip,</li> + * <li>check,</li> + * <li>print,</li> + * <li>showicode,</li> + * <li>log,</li> + * <li>debuginfo.</li> + * </ul> + * It also takes the following parameters as nested elements:<ul> + * <li>src (for srcdir),</li> + * <li>classpath,</li> + * <li>sourcepath,</li> + * <li>bootclasspath,</li> + * <li>extdirs.</li> + * </ul> + * + * @author Gilles Dubochet + */ + class NSC extends MatchingTask { + + private val SCALA_PRODUCT: String = + System.getProperty("scala.product", "Scalac Ant compiler"); + private val SCALA_VERSION: String = + System.getProperty("scala.version", "Unknown version"); + + /** The unique Ant file utilities instance to use in this task. */ + private val fileUtils = FileUtils.newFileUtils(); + + // ################################################################### + // ##### Ant Properties ##### + // ################################################################### + + abstract class PermissibleValue { + val values: List[String]; + def isPermissible(value: String): Boolean = ( + (value == "") || + values.exists(v: String => v startsWith value) + ) + } + + /** Defines valid values for the logging property. */ + object LoggingLevel extends PermissibleValue { + val values = List("none", "verbose", "debug"); + } + + /** Defines valid values for properties that refer to compiler phases. */ + object CompilerPhase extends PermissibleValue { + val values = List( + "namer", "typer", "pickler", "uncurry", "tailcalls", + "transmatch", "explicitouter", "erasure", "lambdalift", + "flatten", "constructors", "mixin", "icode", "jvm", "terminal"); + } + + /** The directories that contain source files to compile. */ + private var origin: Option[Path] = None; + /** The directory to put the compiled files in. */ + private var destination: Option[File] = None; + + /** The class path to use for this compilation. */ + private var classpath: Option[Path] = None; + /** The source path to use for this compilation. */ + private var sourcepath: Option[Path] = None; + /** The boot class path to use for this compilation. */ + private var bootclasspath: Option[Path] = None; + /** The external extensions path to use for this compilation. */ + private var extpath: Option[Path] = None; + + /** The text encoding of the files to compile. */ + private var encoding: Option[String] = None; + + /** How much logging output to print. Either none (default), verbose or debug. */ + private var logging: Option[String] = None; + /** Whether to use implicit predefined values or not. */ + private var usepredefs: Boolean = true; + /** Whether to implicitly import or not. */ + private var useimports: Boolean = true; + /** Whether to force compilation of all files or not. */ + private var force: Boolean = false; + /** After which phase the compilation should stop. */ + private var stop: Option[String] = None; + /** Which compilation phases should be skipped during compilation. */ + private var skip: List[String] = Nil; + /** Which compilation phases should be logged during compilation. */ + private var logPhase: List[String] = Nil; + /** Which compilation phases results should be checked for consistency. */ + private var check: List[String] = Nil; + /** Which compilation phases results should be printed-out. */ + private var print: List[String] = Nil; + /** Print ICode files along with class files (debug option). */ + private var showICode: Boolean = false; + + /** Instruct the compiler to generate debugging information (pass '-g') */ + private var debugInfo: Boolean = false; + + + // ################################################################### + // ##### Properties setters ##### + // ################################################################### + + /** + * Sets the srcdir attribute. Used by Ant. + * @param input The value of <code>origin</code>. + */ + def setSrcdir(input: Path) = + if (origin.isEmpty) + origin = Some(input); + else + origin.get.append(input); + + /** + * Sets the <code>origin</code> as a nested src Ant parameter. + * @return An origin path to be configured. + */ + def createSrc(): Path = { + if (origin.isEmpty) { + origin = Some(new Path(getProject())) + } + origin.get.createPath() + } + + /** + * Sets the <code>origin</code> as an external reference Ant parameter. + * @param input A reference to an origin path. + */ + def setSrcref(input: Reference) = + createSrc().setRefid(input); + + /** + * Gets the value of the origin attribute in a Scala-friendly form. + * @returns The origin path as a list of files. + */ + private def getOrigin: List[File] = + if (origin.isEmpty) + throw new ArrayIndexOutOfBoundsException("Member 'origin' is empty."); + else + List.fromArray(origin.get.list()).map(nameToFile("src")); + + /** + * Sets the destdir attribute. Used by Ant. + * @param input The value of <code>destination</code>. + */ + def setDestdir(input: File) = + destination = Some(input); + + /** + * Gets the value of the destination attribute in a Scala-friendly form. + * @returns The destination as a file. + */ + private def getDestination: File = + if (destination.isEmpty) + throw new ArrayIndexOutOfBoundsException("Member 'destination' is empty."); + else + testReadableFile("destdir")(getProject().resolveFile(destination.get.toString())); + + /** + * Sets the classpath attribute. Used by Ant. + * @param input The value of <code>classpath</code>. + */ + def setClasspath(input: Path) = + if (classpath.isEmpty) + classpath = Some(input); + else + classpath.get.append(input); + + /** + * Sets the <code>classpath</code> as a nested classpath Ant parameter. + * @return A class path to be configured. + */ + def createClasspath(): Path = { + if (classpath.isEmpty) { + classpath = Some(new Path(getProject())) + } + classpath.get.createPath() + } + + /** + * Sets the <code>classpath</code> as an external reference Ant parameter. + * @param input A reference to a class path. + */ + def setClasspathref(input: Reference) = + createClasspath().setRefid(input); + + /** + * Gets the value of the classpath attribute in a Scala-friendly form. + * @returns The class path as a list of files. + */ + private def getClasspath: List[File] = + if (classpath.isEmpty) + throw new ArrayIndexOutOfBoundsException("Member 'classpath' is empty."); + else + List.fromArray(classpath.get.list()).map(nameToFile("classpath")); + + /** + * Sets the sourcepath attribute. Used by Ant. + * @param input The value of <code>sourcepath</code>. + */ + def setSourcepath(input: Path) = + if (sourcepath.isEmpty) + sourcepath = Some(input); + else + sourcepath.get.append(input); + + /** + * Sets the <code>sourcepath</code> as a nested sourcepath Ant parameter. + * @return A source path to be configured. + */ + def createSourcepath(): Path = { + if (sourcepath.isEmpty) { + sourcepath = Some(new Path(getProject())) + } + sourcepath.get.createPath() + } + + /** + * Sets the <code>sourcepath</code> as an external reference Ant parameter. + * @param input A reference to a source path. + */ + def setSourcepathref(input: Reference) = + createSourcepath().setRefid(input); + + /** + * Gets the value of the sourcepath attribute in a Scala-friendly form. + * @returns The source path as a list of files. + */ + private def getSourcepath: List[File] = + if (sourcepath.isEmpty) + throw new ArrayIndexOutOfBoundsException("Member 'sourcepath' is empty."); + else + List.fromArray(sourcepath.get.list()).map(nameToFile("sourcepath")); + + /** + * Sets the boot classpath attribute. Used by Ant. + * @param input The value of <code>bootclasspath</code>. + */ + def setBootclasspath(input: Path) = + if (bootclasspath.isEmpty) + bootclasspath = Some(input); + else + bootclasspath.get.append(input); + + /** + * Sets the <code>bootclasspath</code> as a nested sourcepath Ant parameter. + * @return A source path to be configured. + */ + def createBootclasspath(): Path = { + if (bootclasspath.isEmpty) { + bootclasspath = Some(new Path(getProject())) + } + bootclasspath.get.createPath() + } + + /** + * Sets the <code>bootclasspath</code> as an external reference Ant parameter. + * @param input A reference to a source path. + */ + def setBootclasspathref(input: Reference) = + createBootclasspath().setRefid(input); + + /** + * Gets the value of the bootclasspath attribute in a Scala-friendly form. + * @returns The boot class path as a list of files. + */ + private def getBootclasspath: List[File] = + if (bootclasspath.isEmpty) + throw new ArrayIndexOutOfBoundsException("Member 'bootclasspath' is empty."); + else + List.fromArray(bootclasspath.get.list()).map(nameToFile("bootclasspath")); + + /** + * Sets the external extensions path attribute. Used by Ant. + * @param input The value of <code>extpath</code>. + */ + def setExtdirs(input: Path) = + if (extpath.isEmpty) + extpath = Some(input); + else + extpath.get.append(input); + + /** + * Sets the <code>extpath</code> as a nested sourcepath Ant parameter. + * @return An extensions path to be configured. + */ + def createExtdirs(): Path = { + if (extpath.isEmpty) { + extpath = Some(new Path(getProject())) + } + extpath.get.createPath() + } + + /** + * Sets the <code>extpath</code> as an external reference Ant parameter. + * @param input A reference to an extensions path. + */ + def setExtdirsref(input: Reference) = + createExtdirs().setRefid(input); + + /** + * Gets the value of the extpath attribute in a Scala-friendly form. + * @returns The extensions path as a list of files. + */ + private def getExtpath: List[File] = + if (extpath.isEmpty) + throw new ArrayIndexOutOfBoundsException("Member 'extdirs' is empty."); + else + List.fromArray(extpath.get.list()).map(nameToFile("extdirs")); + + /** + * Sets the encoding attribute. Used by Ant. + * @param input The value of <code>encoding</code>. + */ + def setEncoding(input: String): Unit = + encoding = Some(input); + + /** + * Sets the logging level attribute. Used by Ant. + * @param input The value for <code>logging</code>. + */ + def setLogging(input: String) = + if (LoggingLevel.isPermissible(input)) + logging = Some(input); + else + error("Logging level '" + input + "' does not exist."); + + /** + * Sets the use predefs attribute. Used by Ant. + * @param input The value for <code>usepredefs</code>. + */ + def setUsepredefs(input: Boolean): Unit = + usepredefs = input; + + /** + * Sets the use imports attribute. Used by Ant. + * @param input The value for <code>useimport</code>. + */ + def setUseimports(input: Boolean): Unit = + useimports = input; + + /** + * Sets the force attribute. Used by Ant. + * @param input The value for <code>force</code>. + */ + def setForce(input: Boolean): Unit = + force = input; + + /** + * Sets the force attribute. Used by Ant. + * @param input The value for <code>force</code>. + */ + def setStop(input: String) = + if (CompilerPhase.isPermissible(input)) { + if (input != "") + stop = Some(input); + } + else + error("Phase '" + input + "' in stop does not exist."); + + /** + * Sets the force attribute. Used by Ant. + * @param input The value for <code>force</code>. + */ + def setSkip(input: String) = { + skip = List.fromArray(input.split(",")).flatMap(s: String => { + val st = s.trim(); + if (CompilerPhase.isPermissible(st)) (if (input != "") List(st) else Nil) + else {error("Phase '" + st + "' in skip does not exist."); Nil} + }); + } + + /** + * Sets the log attribute. Used by Ant. + * @param input The value for <code>logPhase</code>. + */ + def setLog(input: String) = { + logPhase = List.fromArray(input.split(",")).flatMap(s: String => { + val st = s.trim(); + if (CompilerPhase.isPermissible(st)) (if (input != "") List(st) else Nil) + else {error("Phase " + st + " in log does not exist."); Nil} + }); + } + + /** + * Sets the check attribute. Used by Ant. + * @param input The value for <code>check</code>. + */ + def setCheck(input: String) = { + check = List.fromArray(input.split(",")).flatMap(s: String => { + val st = s.trim(); + if (CompilerPhase.isPermissible(st)) (if (input != "") List(st) else Nil) + else {error("Phase " + st + " in check does not exist."); Nil} + }); + } + + /** + * Sets the print attribute. Used by Ant. + * @param input The value for <code>print</code>. + */ + def setPrint(input: String) = { + print = List.fromArray(input.split(",")).flatMap(s: String => { + val st = s.trim(); + if (CompilerPhase.isPermissible(st)) (if (input != "") List(st) else Nil) + else {error("Phase " + st + " in print does not exist."); Nil} + }); + } + + def setShowicode(input: Boolean): Unit = + showICode = input; + + /** + * Set the debug info attribute. + */ + def setDebuginfo(input: Boolean): Unit = + debugInfo = input; + + // ################################################################### + // ##### Compilation and support methods ##### + // ################################################################### + + override protected def getDirectoryScanner (baseDir: java.io.File) = + super.getDirectoryScanner(baseDir); + + /** + * Creates a file from a given string. + * @param test A method to test whether the file is valid. + * @param name The path of the file as a string. + * @return The file corresponding to the provided name. + */ + private def nameToFile(test: File=>File)(name: String): File = + test(getProject().resolveFile(name)); + + /** + * Creates a file from a given string. + * @param test A method to test whether the file is valid. + * @param name The path of the file as a string. + * @return The file corresponding to the provided name. + */ + private def nameToFile(test: File=>File, origin: File)(name: String): File = + test(fileUtils.resolveFile(origin, name)); + + /** + * Creates a file from a given string and tests its validity using the <code>testReadableFile</code> method. + * @param pathName The name of the path in which the file is. + * @param name The path of the file as a string. + * @return The file corresponding to the provided name. + */ + private def nameToFile(pathName: String, origin: File)(name: String): File = { + nameToFile((f: File) => testReadableFile(pathName)(f), origin)(name); + } + + /** + * Creates a file from a given string and tests its validity using the <code>testReadableFile</code> method. + * @param pathName The name of the path in which the file is. + * @param name The path of the file as a string. + * @return The file corresponding to the provided name. + */ + private def nameToFile(pathName: String)(name: String): File = { + nameToFile((f: File) => testReadableFile(pathName)(f))(name); + } + + /** + * Tests whether a file is readable (if it does not exist, it is not readable. + * If it is not readable, prints a warning message. + * @param pathName The name of the path in which the file is (used for printing-out warning message). + * @param file The file to test. + * @return The same file as provided. + */ + private def testReadableFile(pathName: String)(file: File): File = { + if (!file.exists()) + log("Element '" + file.toString() + "' in " + pathName + " does not exist.", Project.MSG_WARN); + file + } + + private def asString(path: List[File]): String = { + path.map(file: File => asString(file)).mkString("", File.pathSeparator, "") + } + + private def asString(file: File): String = + file.getAbsolutePath(); + + /** + * Generates a build error. Error location will be the current task in the ant file. + * @param message The message of the error. This message should be end-user readable. + * @throws org.apache.tools.ant.BuildException The build error exception. Will be thrown in all conditions. + */ + private def error(message: String) = { + throw new BuildException(message, getLocation()); + } + + /** + * Performs the compilation. + */ + override def execute() = { + + // Tests if all mandatory attributes are set and valid. + if (origin.isEmpty) error("Attribute 'srcdir' is not set."); + if (getOrigin.isEmpty) error("Attribute 'srcdir' is not set."); + if (!destination.isEmpty && !destination.get.isDirectory()) + error("Attribute 'destdir' does not refer to an existing directory."); + if (destination.isEmpty) { + destination = Some(getOrigin.head); + } + + val mapper = new GlobPatternMapper(); + mapper.setTo("*.class"); + mapper.setFrom("*.scala"); + + // Scans source directories to build up a compile lists. + // If force is false, only files were the .class file in destination is older than + // the .scala file will be used. + val sourceFiles: List[File] = + for (val originDir <- getOrigin; + val originFile <- { + var includedFiles = getDirectoryScanner(originDir).getIncludedFiles(); + if (!force) { + includedFiles = new SourceFileScanner(this) + .restrict(includedFiles, originDir, destination.get, mapper) + } + (List.fromArray(includedFiles)).map(nameToFile("srcdir", originDir)) + } + ) yield { + log(originFile.toString(), Project.MSG_VERBOSE); + originFile + } + + if (sourceFiles.length == 0) + log("No files selected for compilation") + else + log("Compiling " + sourceFiles.length + " source file" + + (if (sourceFiles.length > 1) "s" else "") + + (" to " + getDestination.toString())); + + System.setProperty("scala.library.class.path", ""); + System.setProperty("scala.library.source.path", ""); + + // Builds-up the compilation settings for Scalac with the existing Ant parameters. + val reporter = new ConsoleReporter(); + val settings = new Settings(error); + settings.outdir.value = asString(destination.get); + if (!classpath.isEmpty) settings.classpath.value = asString(getClasspath); + if (!sourcepath.isEmpty) settings.sourcepath.value = asString(getSourcepath) + else if (origin.get.size() > 0) settings.sourcepath.value = origin.get.list()(0); + if (!bootclasspath.isEmpty) settings.bootclasspath.value = asString(getBootclasspath); + if (!extpath.isEmpty) settings.extdirs.value = asString(getExtpath); + if (!encoding.isEmpty) settings.encoding.value = encoding.get; + if (!logging.isEmpty && logging.get == "verbose") { + settings.verbose.value = true; + } + else if (!logging.isEmpty && logging.get == "debug") { + settings.verbose.value = true; + settings.debug.value = true; + } + settings.noimports.value = !useimports; + settings.nopredefs.value = !usepredefs; + if (!stop.isEmpty) settings.stop.value = List(stop.get); + if (!skip.isEmpty) settings.skip.value = skip; + if (!check.isEmpty) settings.check.value = check; + if (!print.isEmpty) settings.print.value = print; + settings.Xshowicode.value = showICode; + settings.debuginfo.value = debugInfo; + if (!logPhase.isEmpty) settings.log.value = logPhase; + + // Sets path properties to prevent ClassPath from being corrupted. + // It this isn't done, classpath will contain more than + //System.setProperty("scala.library.class.path", ""); + //System.setProperty("scala.library.source.path", ""); + + // Compiles the actual code + val compiler = new Global(settings, reporter); + try { + (new compiler.Run).compile(sourceFiles.map(f:File=>f.toString())); + if (reporter.errors > 0) + error("Compile failed with " + + reporter.errors + " error" + + (if (reporter.errors > 1) "s" else "") + + "; see the compiler error output for details."); + } + catch { + case exception @ FatalError(msg) => { + exception.printStackTrace(); + if (settings.debug.value) exception.printStackTrace(); + error("Compile failed because of an internal compiler error (" + + msg + "); see the error output for details."); + } + } + if (reporter.warnings > 0) + log("Compile suceeded with " + + reporter.errors + " warning" + + (if (reporter.warnings > 1) "s" else "") + + "; see the compiler output for details."); + reporter.printSummary() + } + + } + +} diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala new file mode 100644 index 0000000000..9e4821a734 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -0,0 +1,631 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import scala.concurrent._; +import symtab.Flags._; + + +import java.lang.Math; +import java.util.HashMap; +import java.io.StringWriter; + +import javax.swing.tree._; +import javax.swing.event.TreeModelListener; +import javax.swing._; + +import java.awt.BorderLayout; +import java.awt.{List => awtList, _}; +import java.awt.event._; + +import scala.text._; + +/** + * Tree browsers can show the AST in a graphical and interactive + * way, useful for debugging and understanding. + */ +abstract class TreeBrowsers { + + val global: Global; + import global._; + import nme.EMPTY; + + /** Pseudo tree class, so that all JTree nodes are treated uniformly */ + case class ProgramTree(units: List[UnitTree]) extends Tree { + override def toString(): String = "Program"; + } + + /** Pseudo tree class, so that all JTree nodes are treated uniformly */ + case class UnitTree(unit: CompilationUnit) extends Tree { + override def toString(): String = unit.toString(); + } + + def create(): SwingBrowser = new SwingBrowser(); + + /** + * Java Swing pretty printer for Scala abstract syntax trees. + */ + class SwingBrowser { + + def browse(units: Iterator[CompilationUnit]): Unit = + browse(units.toList); + + /** print the whole program */ + def browse(units: List[CompilationUnit]): Unit = { + val phase: Phase = globalPhase; + var unitList: List[UnitTree] = Nil; + + + for (val i <- units) + unitList = UnitTree(i) :: unitList; + val tm = new ASTTreeModel(ProgramTree(unitList)); + + val frame = new BrowserFrame(); + frame.setTreeModel(tm); + + val lock = new Lock(); + frame.createFrame(lock); + + // wait for the frame to be closed + lock.acquire; + } + } + + /** Tree model for abstract syntax trees */ + class ASTTreeModel(val program: ProgramTree) extends TreeModel { + var listeners: List[TreeModelListener] = Nil; + + /** Add a listener to this tree */ + def addTreeModelListener(l : TreeModelListener): Unit = listeners = l :: listeners; + + /** Return the index'th child of parent */ + def getChild(parent: Any, index: Int): AnyRef = { + packChildren(parent).drop(index).head; + } + + /** Return the number of children this 'parent' has */ + def getChildCount(parent: Any): Int = + packChildren(parent).length; + + /** Return the index of the given child */ + def getIndexOfChild(parent: Any, child: Any): Int = + packChildren(parent).dropWhile(c => c != child).length; + + /** Return the root node */ + def getRoot(): AnyRef = program; + + /** Test whether the given node is a leaf */ + def isLeaf(node: Any): Boolean = packChildren(node).length == 0; + + def removeTreeModelListener(l: TreeModelListener): Unit = + listeners remove (x => x == l); + + /** we ignore this message for now */ + def valueForPathChanged(path: TreePath, newValue: Any) = (); + + /** + * Return a list of children for the given node. + */ + def packChildren(t: Any): List[AnyRef] = + TreeInfo.children(t.asInstanceOf[Tree]); + } + + + /** + * A window that can host the Tree widget and provide methods for + * displaying information + */ + class BrowserFrame { + val frame = new JFrame("Scala AST"); + val topLeftPane = new JPanel(new BorderLayout()); + val topRightPane = new JPanel(new BorderLayout()); + val bottomPane = new JPanel(new BorderLayout()); + var splitPane: JSplitPane = _; + var treeModel: TreeModel = _; + + val textArea: JTextArea = new JTextArea(20, 50); + val infoPanel = new TextInfoPanel(); + + + /** Create a frame that displays the AST. + * + * @param lock The lock is used in order to stop the compilation thread + * until the user is done with the tree inspection. Swing creates its + * own threads when the frame is packed, and therefore execution + * would continue. However, this is not what we want, as the tree and + * especially symbols/types would change while the window is visible. + */ + def createFrame(lock: Lock): Unit = { + lock.acquire; // keep the lock until the user closes the window + + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + frame.addWindowListener(new WindowAdapter() { + /** Release the lock, so compilation may resume after the window is closed. */ + override def windowClosed(e: WindowEvent): Unit = lock.release; + }); + + val tree = new JTree(treeModel) { + /** Return the string for a tree node. */ + override def convertValueToText(value: Any, sel: Boolean, + exp: Boolean, leaf: Boolean, + row: Int, hasFocus: Boolean) = { + val Pair(cls, name) = TreeInfo.treeName(value.asInstanceOf[Tree]); + if (name != EMPTY) + cls + "[" + name.toString() + "]"; + else + cls; + + } + } + + tree.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() { + def valueChanged(e: javax.swing.event.TreeSelectionEvent): Unit = { + textArea.setText(e.getPath().getLastPathComponent().toString()); + infoPanel.update(e.getPath().getLastPathComponent()); + } + }); + + val topSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, topLeftPane, topRightPane); + topSplitPane.setResizeWeight(0.5); + + topLeftPane.add(new JScrollPane(tree), BorderLayout.CENTER); + topRightPane.add(new JScrollPane(infoPanel), BorderLayout.CENTER); + + bottomPane.add(new JScrollPane(textArea), BorderLayout.CENTER); + textArea.setFont(new Font("monospaced", Font.PLAIN, 14)); + textArea.setEditable(false); + + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topSplitPane, bottomPane); + frame.getContentPane().add(splitPane); + frame.pack(); + frame.setVisible(true); + } + + def setTreeModel(tm: TreeModel): Unit = treeModel = tm; + } + + /** + * Present detailed information about the selected tree node. + */ + class TextInfoPanel extends JTextArea(30, 40) { + + setFont(new Font("monospaced", Font.PLAIN, 12)); + + def update(v: AnyRef): Unit = { + val t: Tree = v.asInstanceOf[Tree]; + val str = new StringBuffer(); + var buf = new StringWriter(); + + t match { + case ProgramTree(_) => (); + case UnitTree(_) => (); + case _ => + str.append("Symbol: ").append(TreeInfo.symbolText(t)); + str.append("\nSymbol info: \n"); + TreeInfo.symbolTypeDoc(t).format(getWidth() / getColumnWidth(), buf); + str.append(buf.toString()); + str.append("\nSymbol tpe: \n"); + if (t.symbol != null) { + buf = new StringWriter(); + TypePrinter.toDocument(t.symbol.tpe).format(getWidth() / getColumnWidth(), buf); + str.append(buf.toString()); + } + str.append("\nSymbol Attributes: \n").append(TreeInfo.symbolAttributes(t)); + str.append("\nType: \n").append(if (t.tpe ne null) t.tpe.toString() else ""); + } + setText(str.toString()); + } + } + + + /** Computes different information about a tree node. It + * is used as central place to do all pattern matching against + * Tree. + */ + object TreeInfo { + + /** Return the case class name and the Name, if the node defines one */ + def treeName(t: Tree): Pair[String, Name] = t match { + case ProgramTree(units) => + Pair("Program", EMPTY); + + case UnitTree(unit) => + Pair("CompilationUnit", unit.toString()); + + case Attributed(attribute, definition) => + Pair("Attributed", EMPTY); + + case DocDef(comment, definition) => + Pair("DocDef", EMPTY); + + case ClassDef(mods, name, tparams, tpt, impl) => + Pair("ClassDef", name); + + case PackageDef(packaged, impl) => + Pair("PackageDef", EMPTY); + + case ModuleDef(mods, name, impl) => + Pair("ModuleDef", name); + + case ValDef(mods, name, tpe, rhs) => + Pair("ValDef", name); + + case DefDef(mods, name, tparams, vparams, tpe, rhs) => + Pair("DefDef", name); + + case AbsTypeDef(mods, name, rhs, lobound) => + Pair("AbsTypeDef", name); + + case AliasTypeDef(mods, name, tparams, rhs) => + Pair("AliasTypeDef", name); + + case Import(expr, selectors) => + Pair("Import", EMPTY); + + case CaseDef(pat, guard, body) => + Pair("CaseDef", EMPTY); + + case Template(parents, body) => + Pair("Template", EMPTY); + + case LabelDef(name, params, rhs) => + Pair("LabelDef", name); + + case Block(stats, expr) => + Pair("Block", EMPTY); + + case Sequence(trees) => + Pair("Sequence", EMPTY); + + case Alternative(trees) => + Pair("Alternative", EMPTY); + + case Bind(name, rhs) => + Pair("Bind", name); + + case Match(selector, cases) => + Pair("Visitor", EMPTY); + + case Function(vparams, body) => + Pair("Function", EMPTY); + + case Assign(lhs, rhs) => + Pair("Assign", EMPTY); + + case If(cond, thenp, elsep) => + Pair("If", EMPTY); + + case Return(expr) => + Pair("Return", EMPTY); + + case Throw(expr) => + Pair("Throw", EMPTY); + + case New(init) => + Pair("New", EMPTY); + + case Typed(expr, tpe) => + Pair("Typed", EMPTY); + + case TypeApply(fun, args) => + Pair("TypeApply", EMPTY); + + case Apply(fun, args) => + Pair("Apply", EMPTY); + + case Super(qualif, mixin) => + Pair("Super", qualif.toString() + ", mixin: " + mixin.toString()); + + case This(qualifier) => + Pair("This", qualifier); + + case Select(qualifier, selector) => + Pair("Select", selector); + + case Ident(name) => + Pair("Ident", name); + + case Literal(value) => + Pair("Literal", EMPTY); + + case TypeTree() => + Pair("TypeTree", EMPTY); + + case SingletonTypeTree(ref) => + Pair("SingletonType", EMPTY); + + case SelectFromTypeTree(qualifier, selector) => + Pair("SelectFromType", selector); + + case CompoundTypeTree(template) => + Pair("CompoundType", EMPTY); + + case AppliedTypeTree(tpe, args) => + Pair("AppliedType", EMPTY); + + case Try(block, catcher, finalizer) => + Pair("Try", EMPTY); + + case EmptyTree => + Pair("Empty", EMPTY); + + case ArrayValue(elemtpt, trees) => + Pair("ArrayValue", EMPTY); + } + + /** Return a list of children for the given tree node */ + def children(t: Tree): List[Tree] = t match { + case ProgramTree(units) => + units; + + case UnitTree(unit) => + List(unit.body); + + case Attributed(attribute, definition) => + List(attribute, definition); + + case DocDef(comment, definition) => + List(definition); + + case ClassDef(mods, name, tparams, tpt, impl) => { + var children: List[Tree] = List(); + children = tparams ::: children; + tpt :: impl :: children + } + + case PackageDef(name, stats) => + stats; + + case ModuleDef(mods, name, impl) => + List(impl); + + case ValDef(mods, name, tpe, rhs) => + List(tpe, rhs); + + case DefDef(mods, name, tparams, vparams, tpe, rhs) => { + var children: List[Tree] = List(); + children = tparams ::: children; + children = List.flatten(vparams) ::: children; + tpe :: rhs :: children + } + + case AbsTypeDef(mods, name, rhs, lobound) => + List(rhs, lobound); + + case AliasTypeDef(mods, name, tparams, rhs) => { + var children: List[Tree] = List(); + children = tparams ::: children; + rhs :: children + } + + case Import(expr, selectors) => { + var children: List[Tree] = List(expr); + children + } + + case CaseDef(pat, guard, body) => + List(pat, guard, body); + + case Template(parents, body) => + parents ::: body; + + case LabelDef(name, params, rhs) => + params ::: List(rhs); + + case Block(stats, expr) => + stats ::: List(expr); + + case Sequence(trees) => + trees; + + case Alternative(trees) => + trees; + + case Bind(name, rhs) => + List(rhs); + + case Match(selector, cases) => + selector :: cases; + + case Function(vparams, body) => + vparams ::: List(body); + + case Assign(lhs, rhs) => + List(lhs, rhs); + + case If(cond, thenp, elsep) => + List(cond, thenp, elsep); + + case Return(expr) => + List(expr); + + case Throw(expr) => + List(expr); + + case New(init) => + List(init); + + case Typed(expr, tpe) => + List(expr, tpe); + + case TypeApply(fun, args) => + List(fun) ::: args; + + case Apply(fun, args) => + List(fun) ::: args; + + case Super(qualif, mixin) => + Nil; + + case This(qualif) => + Nil + + case Select(qualif, selector) => + List(qualif); + + case Ident(name) => + Nil; + + case Literal(value) => + Nil; + + case TypeTree() => + Nil; + + case SingletonTypeTree(ref) => + List(ref); + + case SelectFromTypeTree(qualif, selector) => + List(qualif); + + case CompoundTypeTree(templ) => + List(templ); + + case AppliedTypeTree(tpe, args) => + tpe :: args; + + case Try(block, catches, finalizer) => + block :: catches ::: List(finalizer); + + case ArrayValue(elemtpt, elems) => + elemtpt :: elems; + + case EmptyTree => + Nil; + } + + /** Return a textual representation of this t's symbol */ + def symbolText(t: Tree): String = { + var prefix = ""; + + if (t.hasSymbol) + prefix = "[has] "; + if (t.isDef) + prefix = "[defines] "; + + prefix + t.symbol + } + + /** Return t's symbol type */ + def symbolTypeDoc(t: Tree): Document = { + val s = t.symbol; + if (s != null) + TypePrinter.toDocument(s.info); + else + DocNil; + } + + /** Return a textual representation of (some of) the symbol's + * attributes */ + def symbolAttributes(t: Tree): String = { + val s = t.symbol; + var att = ""; + + if (s != null) { + var str = flagsToString(s.flags); + if (s.hasFlag(STATIC) || s.hasFlag(STATICMEMBER)) + str = str + " isStatic "; + str + } + else ""; + } + } + + object TypePrinter { + + ///////////////// Document pretty printer //////////////// + + implicit def view(n: String): Document = DocText(n); + + def toDocument(sym: Symbol): Document = + toDocument(sym.info); + + def symsToDocument(syms: List[Symbol]): Document = syms match { + case Nil => DocNil; + case s :: Nil => Document.group(toDocument(s)); + case _ => + Document.group( + syms.tail.foldLeft (toDocument(syms.head) :: ", ") ( + (d: Document, s2: Symbol) => toDocument(s2) :: ", " :/: d) ); + } + + def toDocument(ts: List[Type]): Document = ts match { + case Nil => DocNil; + case t :: Nil => Document.group(toDocument(t)); + case _ => + Document.group( + ts.tail.foldLeft (toDocument(ts.head) :: ", ") ( + (d: Document, t2: Type) => toDocument(t2) :: ", " :/: d) ); + } + + def toDocument(t: Type): Document = t match { + case ErrorType => "ErrorType()"; + case WildcardType => "WildcardType()"; + case NoType => "NoType()"; + case NoPrefix => "NoPrefix()"; + case ThisType(s) => "ThisType(" + s.name + ")"; + + case SingleType(pre, sym) => + Document.group( + Document.nest(4, "SingleType(" :/: + toDocument(pre) :: ", " :/: sym.name.toString() :: ")") + ); + + case ConstantType(value) => + "ConstantType(" + value + ")"; + + case TypeRef(pre, sym, args) => + Document.group( + Document.nest(4, "TypeRef(" :/: + toDocument(pre) :: ", " :/: + sym.name.toString() :: ", " :/: + "[ " :: toDocument(args) ::"]" :: ")") + ); + + case TypeBounds(lo, hi) => + Document.group( + Document.nest(4, "TypeBounds(" :/: + toDocument(lo) :: ", " :/: + toDocument(hi) :: ")") + ); + + case RefinedType(parents, defs) => + Document.group( + Document.nest(4, "RefinedType(" :/: + toDocument(parents) :: ")") + ); + + case ClassInfoType(parents, defs, clazz) => + Document.group( + Document.nest(4,"ClassInfoType(" :/: + toDocument(parents) :: ", " :/: + clazz.name.toString() :: ")") + ); + + case MethodType(paramtypes, result) => + Document.group( + Document.nest(4, "MethodType(" :/: + Document.group("(" :/: + toDocument(paramtypes) :/: + "), ") :/: + toDocument(result) :: ")") + ); + + case PolyType(tparams, result) => + Document.group( + Document.nest(4,"PolyType(" :/: + Document.group("(" :/: + symsToDocument(tparams) :/: + "), ") :/: + toDocument(result) :: ")") + ); + + case _ => abort("Unknown case: " + t.toString()); + } + } + +} diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala new file mode 100644 index 0000000000..425cf9eac8 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -0,0 +1,158 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import scala.tools.nsc.util.Position; +import symtab.Flags._; + +abstract class TreeGen { + + val global: Global; + + import global._; + import definitions._; + import posAssigner.atPos; + + /** Builds a reference to value whose type is given stable prefix. + */ + def mkQualifier(tpe: Type): Tree = tpe match { + case NoPrefix => + EmptyTree + case ThisType(clazz) => + if (clazz.isRoot || clazz.isEmptyPackageClass) EmptyTree else This(clazz) + case SingleType(pre, sym) => + if (sym.isThisSkolem) { + mkQualifier(ThisType(sym.deSkolemize)) + } else { + val qual = mkStableRef(pre, sym); + qual.tpe match { + case MethodType(List(), restpe) => + Apply(qual, List()) setType restpe + case _ => + qual + } + } + case TypeRef(pre, sym, args) => + assert(phase.erasedTypes); + if (sym.isModuleClass && !sym.isRoot) { + val qual = Select(mkQualifier(sym.owner.tpe), sym.sourceModule); + qual.tpe match { + case MethodType(List(), restpe) => + Apply(qual, List()) setType restpe + case _ => + qual + } + } else This(sym) + } + + /** Builds a reference to given symbol with given stable prefix. */ + def mkRef(pre: Type, sym: Symbol): Tree = { + val qual = mkQualifier(pre); + if (qual == EmptyTree) Ident(sym) else Select(qual, sym) + } + + /** Builds a reference to given symbol. */ + def mkRef(sym: Symbol): Tree = + if (sym.owner.isClass) mkRef(sym.owner.thisType, sym) else Ident(sym); + + /** Replaces tree type with a stable type if possible */ + def stabilize(tree: Tree): Tree = tree match { + case Ident(_) => + if (tree.symbol.isStable) tree.setType(singleType(tree.symbol.owner.thisType, tree.symbol)) + else tree + case Select(qual, _) => + if (tree.symbol.isStable && qual.tpe.isStable) tree.setType(singleType(qual.tpe, tree.symbol)) + else tree + case _ => + tree + } + + /** Cast `tree' to type `pt' */ + def cast(tree: Tree, pt: Type): Tree = { + if (settings.debug.value) log("casting " + tree + ":" + tree.tpe + " to " + pt); + assert(!tree.tpe.isInstanceOf[MethodType], tree); + typer.typed { + atPos(tree.pos) { + Apply(TypeApply(Select(tree, Object_asInstanceOf), List(TypeTree(pt))), List()) + } + } + } + + /** Builds a reference with stable type to given symbol */ + def mkStableRef(pre: Type, sym: Symbol): Tree = stabilize(mkRef(pre, sym)); + def mkStableRef(sym: Symbol): Tree = stabilize(mkRef(sym)); + + def This(sym: Symbol): Tree = + global.This(sym.name) setSymbol sym setType sym.thisType; + + def Ident(sym: Symbol): Tree = { + assert(sym.isTerm); + global.Ident(sym.name) setSymbol sym setType sym.tpe; + } + + def Select(qual: Tree, sym: Symbol): Tree = + if (qual.symbol != null && + (qual.symbol.name.toTermName == nme.ROOT || + qual.symbol.name.toTermName == nme.EMPTY_PACKAGE_NAME)) { + this.Ident(sym) + } else { + assert(sym.isTerm); + val result = global.Select(qual, sym.name) setSymbol sym; + if (qual.tpe != null) result setType qual.tpe.memberType(sym); + result + } + + /** Builds an instance test with given value and type. */ + def mkIsInstanceOf(value: Tree, tpe: Type, erased: Boolean): Tree = { + val sym = + if(erased) + definitions.Any_isInstanceOfErased + else + definitions.Any_isInstanceOf; + Apply( + TypeApply( + Select(value, sym), + List(TypeTree(tpe))), + List()) + } + + def mkIsInstanceOf(value: Tree, tpe: Type): Tree = { + mkIsInstanceOf(value, tpe, global.phase.erasedTypes); + } + + /** Builds a cast with given value and type. */ + def mkAsInstanceOf(value: Tree, tpe: Type, erased: Boolean): Tree = { + val sym = + if(erased) + definitions.Any_asInstanceOfErased + else + definitions.Any_asInstanceOf; + + Apply( + TypeApply( + Select(value, sym), + List(TypeTree(tpe))), + List()) + } + + def mkAsInstanceOf(value: Tree, tpe: Type): Tree = { + mkAsInstanceOf(value, tpe, global.phase.erasedTypes); + } + + + /** Builds a list with given head and tail. */ + def mkNewCons(head: Tree, tail: Tree): Tree = + New(Apply(mkRef(definitions.ConsClass), List(head,tail))); + + /** Builds a list with given head and tail. */ + def mkNil: Tree = + mkRef(definitions.NilModule); + + /** Builds a pair */ + def mkNewPair(left: Tree, right: Tree) = + New(Apply(mkRef(definitions.TupleClass(2)), List(left,right))); + +} diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala new file mode 100644 index 0000000000..dedfc6b03a --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala @@ -0,0 +1,187 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import symtab.Flags._; + +abstract class TreeInfo { + + val global: Global; + import global._; + + def isTerm(tree: Tree): boolean = tree.isTerm; + def isType(tree: Tree): boolean = tree.isType; + + def isOwnerDefinition(tree: Tree): boolean = tree match { + case PackageDef(_, _) + | ClassDef(_, _, _, _, _) + | ModuleDef(_, _, _) + | DefDef(_, _, _, _, _, _) + | Import(_, _) => true + case _ => false + } + + def isDefinition(tree: Tree): boolean = tree.isDef; + + def isDeclaration(tree: Tree): boolean = tree match { + case DefDef(_, _, _, _, _, EmptyTree) + | ValDef(_, _, _, EmptyTree) + | AbsTypeDef(_, _, _, _) + | AliasTypeDef(_, _, _, _) => true + case _ => false + } + + /** Is tree legal as a member definition of an interface? + */ + def isInterfaceMember(tree: Tree): boolean = tree match { + case EmptyTree => true + case Import(_, _) => true + case AbsTypeDef(_, _, _, _) => true + case AliasTypeDef(_, _, _, _) => true + case DefDef(mods, _, _, _, _, __) => mods.hasFlag(DEFERRED) + case ValDef(mods, _, _, _) => mods.hasFlag(DEFERRED) + case DocDef(_, definition) => isInterfaceMember(definition) + case Attributed(_, definition) => isInterfaceMember(definition) + case _ => false + } + + + /** Is tree a pure definition? + */ + def isPureDef(tree: Tree): boolean = tree match { + case EmptyTree + | ClassDef(_, _, _, _, _) + | ModuleDef(_, _, _) + | AbsTypeDef(_, _, _, _) + | AliasTypeDef(_, _, _, _) + | Import(_, _) + | DefDef(_, _, _, _, _, _) => + true + case ValDef(mods, _, _, rhs) => + mods.hasFlag(MUTABLE) && isPureExpr(rhs) + case DocDef(_, definition) => + isPureDef(definition) + case Attributed(_, definition) => + isPureDef(definition) + case _ => + false + } + + /** Is tree a stable & pure expression? + */ + def isPureExpr(tree: Tree): boolean = tree match { + case EmptyTree + | This(_) + | Super(_, _) + | Literal(_) => + true + case Ident(_) => + tree.symbol.isStable + case Select(qual, _) => + tree.symbol.isStable && isPureExpr(qual) + case TypeApply(fn, _) => + isPureExpr(fn) + case Apply(fn, List()) => + isPureExpr(fn) + case Typed(expr, _) => + isPureExpr(expr) + case Block(stats, expr) => + (stats forall isPureDef) && isPureExpr(expr) + case _ => + false + } + + /** Is tree a self constructor call? + */ + def isSelfConstrCall(tree: Tree): boolean = tree match { + case Ident(nme.CONSTRUCTOR) => + true + case TypeApply(constr, _) => + isSelfConstrCall(constr) + case Apply(constr, _) => + isSelfConstrCall(constr) + case _ => + false + } + + /** Is tree a variable pattern */ + def isVarPattern(pat: Tree): boolean = pat match { + case Ident(name) => isVariableName(name) + case _ => false + } + + /** The longest statement suffix that starts with a constructor */ + def firstConstructor(stats: List[Tree]): Tree = stats.head match { + case constr @ DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => constr + case _ => firstConstructor(stats.tail) + } + + /** Is name a left-associative operator? */ + def isLeftAssoc(operator: Name): boolean = + operator.length > 0 && operator(operator.length - 1) != ':'; + + /** Is name a variable name? */ + def isVariableName(name: Name): boolean = { + val first = name(0); + ((('a' <= first && first <= 'z') || first == '_') + && name != nme.false_ + && name != nme.true_ + && name != nme.null_) + } + + /** Is tree a this node which belongs to `enclClass'? */ + def isSelf(tree: Tree, enclClass: Symbol): boolean = tree match { + case This(_) => tree.symbol == enclClass + case _ => false + } + + /** Is this pattern node a catch-all (wildcard or variable) pattern? */ + def isDefaultCase(cdef: CaseDef) = cdef match { + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case CaseDef(Bind(_, Ident(nme.WILDCARD)), EmptyTree, _) => true + case _ => false + } + + /** Is this pattern node a catch-all or type-test pattern? */ + def isCatchCase(cdef: CaseDef) = cdef match { + case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) => isSimple(tpt.tpe) + case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) => isSimple(tpt.tpe) + case _ => isDefaultCase(cdef) + } + + private def isSimple(tp: Type): boolean = true; + /* If we have run-time types, and these are used for pattern matching, + we should replace this by something like: + + tp match { + case TypeRef(pre, sym, args) => + args.isEmpty && (sym.owner.isPackageClass || isSimple(pre)) + case NoPrefix => + true + case _ => + false + } +*/ + + /** Is this pattern node a sequence-valued pattern? */ + def isSequenceValued(tree: Tree): boolean = tree match { + case Bind(_, body) => isSequenceValued(body) + case Sequence(_) => true + case ArrayValue(_, _) => true + case Star(_) => true + case Alternative(ts) => ts exists isSequenceValued + case _ => false + } + + /** The method part of an application node + */ + def methPart(tree: Tree): Tree = tree match { + case Apply(fn, _) => methPart(fn) + case TypeApply(fn, _) => methPart(fn) + case AppliedTypeTree(fn, _) => methPart(fn) + case _ => tree + } +} diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala new file mode 100644 index 0000000000..206fa58061 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala @@ -0,0 +1,303 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import java.io._; +import symtab.Flags._; + +abstract class TreePrinters { + + val global: Global; + import global._; + + class TreePrinter(out: PrintWriter) { + protected var indentMargin = 0; + protected val indentStep = 2; + protected var indentString = " "; + + def flush = out.flush(); + + def indent = indentMargin = indentMargin + indentStep; + def undent = indentMargin = indentMargin - indentStep; + + def println = { + out.println(); + while (indentMargin > indentString.length()) + indentString = indentString + indentString; + if (indentMargin > 0) + out.write(indentString, 0, indentMargin); + } + + def printSeq[a](ls: List[a])(printelem: a => unit)(printsep: => unit): unit = ls match { + case List() => + case List(x) => printelem(x) + case x :: rest => printelem(x); printsep; printSeq(rest)(printelem)(printsep) + } + + def printColumn(ts: List[Tree], start: String, sep: String, end: String): unit = { + print(start); indent; println; + printSeq(ts){print}{print(sep); println}; undent; println; print(end) + } + + def printRow(ts: List[Tree], start: String, sep: String, end: String): unit = { + print(start); printSeq(ts){print}{print(sep)}; print(end) + } + + def printRow(ts: List[Tree], sep: String): unit = printRow(ts, "", sep, ""); + + def printTypeParams(ts: List[AbsTypeDef]): unit = + if (!ts.isEmpty) { + print("["); printSeq(ts){printParam}{print(", ")}; print("]") + } + + def printValueParams(ts: List[ValDef]): unit = { + print("("); + if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, nme.EMPTY.toTypeName); + printSeq(ts){printParam}{print(", ")}; + print(")") + } + + def printParam(tree: Tree): unit = tree match { + case ValDef(mods, name, tp, rhs) => + print(symName(tree, name)); printOpt(": ", tp); + case AbsTypeDef(mods, name, lo, hi) => + print(symName(tree, name)); + printOpt(" >: ", lo); printOpt(" <: ", hi); + } + + def printBlock(tree: Tree): unit = tree match { + case Block(_, _) => print(tree) + case _ => printColumn(List(tree), "{", ";", "}") + } + + def symName(tree: Tree, name: Name): String = + if (tree.symbol != NoSymbol) tree.symbol.nameString else name.toString(); + + def printOpt(prefix: String, tree: Tree): unit = + if (!tree.isEmpty) { print(prefix); print(tree) } + + def printModifiers(tree: Tree, mods: Modifiers): unit = { + if (tree.symbol == NoSymbol) + printFlags(mods.flags, mods.privateWithin) + else if (tree.symbol.privateWithin == null) + printFlags(tree.symbol.flags, nme.EMPTY.toTypeName) + else + printFlags(tree.symbol.flags, tree.symbol.privateWithin.name) + } + + def printFlags(flags: long, privateWithin: Name): unit = { + val mask = if (settings.debug.value) -1 else PrintableFlags; + val suffixes: List[Pair[long, String]] = + if (privateWithin.isEmpty) List() else List(Pair(PRIVATE, privateWithin.toString())); + val s = flagsToString(flags & mask, suffixes); + if (s.length() != 0) print(s + " ") + } + + def print(str: String): unit = out.print(str); + def print(name: Name): unit = print(name.toString()); + + def printRaw(tree: Tree): unit = { + tree match { + case EmptyTree => + print("<empty>"); + + case ClassDef(mods, name, tparams, tp, impl) => + printModifiers(tree, mods); + print((if (mods.hasFlag(TRAIT)) "trait " else "class ") + symName(tree, name)); + printTypeParams(tparams); + printOpt(": ", tp); print(" extends "); print(impl); + + case PackageDef(packaged, stats) => + print("package "); print(packaged); printColumn(stats, " {", ";", "}") + + case ModuleDef(mods, name, impl) => + printModifiers(tree, mods); print("object " + symName(tree, name)); + print(" extends "); print(impl); + + case ValDef(mods, name, tp, rhs) => + printModifiers(tree, mods); + print(if (mods.hasFlag(MUTABLE)) "var " else "val "); + print(symName(tree, name)); + printOpt(": ", tp); + if (!mods.hasFlag(DEFERRED)) { + print(" = "); + if (rhs.isEmpty) print("_") else print(rhs) + } + + case DefDef(mods, name, tparams, vparamss, tp, rhs) => + printModifiers(tree, mods); + print("def " + symName(tree, name)); + printTypeParams(tparams); vparamss foreach printValueParams; + printOpt(": ", tp); printOpt(" = ", rhs); + + case AbsTypeDef(mods, name, lo, hi) => + printModifiers(tree, mods); print("type "); printParam(tree); + + case AliasTypeDef(mods, name, tparams, rhs) => + printModifiers(tree, mods); print("type " + symName(tree, name)); + printTypeParams(tparams); printOpt(" = ", rhs); + + case LabelDef(name, params, rhs) => + print(symName(tree, name)); printRow(params, "(", ",", ")"); printBlock(rhs); + + case Import(expr, selectors) => + def selectorToString(s: Pair[Name, Name]): String = + if (s._1 == nme.WILDCARD || s._1 == s._2) s._1.toString() + else s._1.toString() + "=>" + s._2.toString(); + print("import "); print(expr); + print(selectors.map(selectorToString).mkString(".{", ", ", "}")) + + case Attributed(attr, definition) => + print("["); print(attr); print("]"); println; print(definition); + + case DocDef(comment, definition) => + print(comment); println; print(definition); + + case Template(parents, body) => + printRow(parents, " with "); + if (!body.isEmpty) printColumn(body, " {", ";", "}") + + case Block(stats, expr) => + printColumn(stats ::: List(expr), "{", ";", "}") + + case Match(selector, cases) => + print(selector); printColumn(cases, " match {", "", "}") + + case CaseDef(pat, guard, body) => + print("case "); print(pat); printOpt(" if ", guard); + print(" => "); print(body) + + case Sequence(trees) => + printRow(trees, "[", ", ", "]") + + case Alternative(trees) => + printRow(trees, "(", "| ", ")") + + case Star(elem) => + print("("); print(elem); print(")*"); + + case Bind(name, t) => + print("("); print(symName(tree, name)); print(" @ "); print(t); print(")"); + + case ArrayValue(elemtpt, trees) => + print("Array["); print(elemtpt); printRow(trees, "]{", ", ", "}") + + case Function(vparams, body) => + print("("); printValueParams(vparams); print(" => "); print(body); print(")") + + case Assign(lhs, rhs) => + print(lhs); print(" = "); print(rhs) + + case If(cond, thenp, elsep) => + print("if ("); print(cond); print(")"); indent; println; + print(thenp); undent; + if (!elsep.isEmpty) { + println; print("else"); indent; println; print(elsep); undent + } + + case Return(expr) => + print("return "); print(expr) + + case Try(block, catches, finalizer) => + print("try "); printBlock(block); + if (!catches.isEmpty) printColumn(catches, " catch {", "", "}"); + printOpt(" finally ", finalizer) + + case Throw(expr) => + print("throw "); print(expr) + + case New(tpe) => + print("new "); print(tpe) + + case Typed(expr, tp) => + print("("); print(expr); print(") : "); print(tp); + + case TypeApply(fun, targs) => + print(fun); printRow(targs, "[", ", ", "]"); + + case Apply(fun, vargs) => + print(fun); printRow(vargs, "(", ", ", ")"); + + case Super(qual, mixin) => + if (!qual.isEmpty || tree.symbol != NoSymbol) print(symName(tree, qual) + "."); + print("super"); + if (!mixin.isEmpty) + print("[" + mixin + "]") + + case This(qual) => + if (!qual.isEmpty) print(symName(tree, qual) + "."); + print("this"); + + case Select(qualifier, name) => + print(qualifier); print("."); print(symName(tree, name)) + + case Ident(name) => + print(symName(tree, name)) + + case Literal(x) => + print(x.tag match { + case NullTag => "null" + case StringTag => "\"" + x.stringValue + "\"" + case CharTag => "\'" + x.charValue + "\'" + case LongTag => x.longValue.toString() + "L"; + case _ => x.value.toString() + }) + + case TypeTree() => + if (tree.tpe == null) print("<type ?>") + else print(tree.tpe.toString()); + + case SingletonTypeTree(ref) => + print(ref); print(".type") + + case SelectFromTypeTree(qualifier, selector) => + print(qualifier); print("#"); print(symName(tree, selector)) + + case CompoundTypeTree(templ) => + print(templ) + + case AppliedTypeTree(tp, args) => + print(tp); printRow(args, "[", ", ", "]") + } + if (global.settings.printtypes.value && tree.isTerm && !tree.isEmpty) { + print("{"); print(if (tree.tpe == null) "<null>" else tree.tpe.toString()); print("}") + } + } + + def print(tree: Tree): unit = + printRaw( + if (tree.isDef && tree.symbol != NoSymbol) { + tree match { + case ClassDef(_, _, _, _, impl) => ClassDef(tree.symbol, impl) + case ModuleDef(_, _, impl) => ModuleDef(tree.symbol, impl) +// case ValDef(_, _, _, rhs) => ValDef(tree.symbol, rhs) + case DefDef(_, _, _, vparamss, _, rhs) => DefDef(tree.symbol, vparamss, rhs) + case AbsTypeDef(_, _, _, _) => AbsTypeDef(tree.symbol) + case AliasTypeDef(_, _, _, rhs) => AliasTypeDef(tree.symbol, rhs) + case _ => tree + } + } else tree); + + def print(unit: CompilationUnit): unit = { + print("// Scala source: " + unit.source + "\n"); + if (unit.body != null) { + print(unit.body); println + } else { + print("<null>") + } + println; flush + } + + def printAll(): unit = { + print("[[syntax trees at end of " + phase + "]]"); + for (val unit <- global.currentRun.units) print(unit) + } + } + + def create(writer: PrintWriter): TreePrinter = new TreePrinter(writer); + def create(stream: OutputStream): TreePrinter = create(new PrintWriter(stream)); + def create(): TreePrinter = create(System.out); +} diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala new file mode 100644 index 0000000000..92b147c947 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -0,0 +1,1251 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import scala.tools.nsc.symtab.Flags; +import java.io.StringWriter; +import java.io.PrintWriter; +import scala.tools.nsc.util.{Position,SourceFile}; +import symtab.Flags._; + +[_trait_] abstract class Trees: Global { + + //statistics + var nodeCount = 0; + + case class Modifiers(flags: int, privateWithin: Name) { + def isPrivate = ((flags & PRIVATE ) != 0); + def isProtected = ((flags & PROTECTED) != 0); + def isVariable = ((flags & MUTABLE) != 0); + def isPublic = !isPrivate && !isProtected; + def hasFlag(flag: int) = (flags & flag) != 0; + def | (flag: int): Modifiers = { + val flags1 = flags | flag; + if (flags1 == flags) this else Modifiers(flags1, privateWithin) + } + } + + def Modifiers(flags: int): Modifiers = Modifiers(flags, nme.EMPTY.toTypeName); + def Modifiers(flags: long): Modifiers = Modifiers(flags.asInstanceOf[int]); + + val NoMods = Modifiers(0); + + abstract class Tree { + + if (util.Statistics.enabled) nodeCount = nodeCount + 1; + + private var posx: int = Position.NOPOS; + + def pos = posx; + + var tpe: Type = _; + + def setPos(p: int): this.type = { posx = p; this } + def setType(tp: Type): this.type = { tpe = tp; this } + + def symbol: Symbol = null; + def symbol_=(sym: Symbol): unit = + throw new Error("symbol_= inapplicable for " + this); + def setSymbol(sym: Symbol): this.type = { symbol = sym; this } + + def hasSymbol = false; + def isDef = false; + def isTerm = false; + def isType = false; + def isEmpty = false; + + override def toString(): String = { + val buffer = new StringWriter(); + val printer = treePrinters.create(new PrintWriter(buffer)); + printer.print(this); printer.flush; + buffer.toString() + } + + override def hashCode(): int = super.hashCode(); + + override def equals(that: Any): boolean = that match { + case t: Tree => this eq t + case _ => false + } + + def duplicate: this.type = (duplicator transform this).asInstanceOf[this.type]; + + def copyAttrs(tree: Tree): this.type = { + posx = tree.posx; + tpe = tree.tpe; + if (hasSymbol) symbol = tree.symbol; + this + } + } + + [_trait_] abstract class SymTree extends Tree { + override def hasSymbol = true; + override var symbol: Symbol = NoSymbol; + } + + abstract class DefTree extends SymTree { + def name: Name; + override def isDef = true; + } + + trait TermTree extends Tree { + override def isTerm = true + } + + trait TypTree extends Tree { + override def isType = true + } + +// ----- auxiliary objects and methods ------------------------------ + + private val duplicator = new Transformer { + override val copy = new StrictTreeCopier; + } + + private def syntheticParams(owner: Symbol, formals: List[Type]): List[Symbol] = { + var cnt = 0; + def freshName() = { cnt = cnt + 1; newTermName("x$" + cnt) } + for (val formal <- formals) yield + owner.newValueParameter(owner.pos, freshName()).setInfo(formal); + } + + private def syntheticParams(owner: Symbol, mtp: Type): List[List[Symbol]] = mtp match { + case PolyType(_, restp) => + syntheticParams(owner, restp) + case MethodType(formals, restp) => + syntheticParams(owner, formals) :: syntheticParams(owner, restp) + case _ => + List() + } + +// def nextPhase = if (phase.id > globalPhase.id) phase else phase.next; + +// ----- tree node alternatives -------------------------------------- + + /** The empty tree */ + case object EmptyTree extends TermTree { + tpe = NoType; + override def isEmpty = true; + } + + abstract class MemberDef extends DefTree { + def mods: Modifiers; + def name: Name; + def keyword : String; + final def hasFlag(mask: long): boolean = (mods.flags & mask) != 0; + + def namePos(source : SourceFile) : Int = if (pos == Position.NOPOS) Position.NOPOS else { + assert(keyword != null); + if (!source.beginsWith(pos, keyword + " ")) { + val p = new Position(source, pos); + // System.err.println("SYM=" + symbol + " KW=" + keyword + " LINE=" + p.lineContent + " TEXT=" + source.content(pos) + source.content(pos + 1)); + if (true) return Position.NOPOS; + else throw new Error(); + } + source.skipWhitespace(pos + keyword.length() + 1); + } + } + + /** Package clause */ + case class PackageDef(name: Name, stats: List[Tree]) + extends MemberDef { + def mods = NoMods; + def keyword = "package"; + } + + def PackageDef(sym: Symbol, stats: List[Tree]): PackageDef = + PackageDef(sym.name, stats) setSymbol sym; + + + abstract class ImplDef extends MemberDef { + def impl: Template + } + + /** Class definition */ + case class ClassDef(mods: Modifiers, name: Name, tparams: List[AbsTypeDef], tpt: Tree, impl: Template) + extends ImplDef { + def keyword = "class"; + } + + def ClassDef(sym: Symbol, impl: Template): ClassDef = + posAssigner.atPos(sym.pos) { + ClassDef(Modifiers(sym.flags), + sym.name, + sym.typeParams map AbsTypeDef, + if (sym.thisSym == sym) EmptyTree else TypeTree(sym.typeOfThis), + impl) setSymbol sym + } + + /** Construct class definition with given class symbol, value parameters, supercall arguments + * and template body. + * @param sym the class symbol + * @param vparamss the value parameters -- if they have symbols they should be owned by `sym' + * @param argss the supercall arguments + * @param body the template statements without primary constructor and value parameter fields. + */ + def ClassDef(sym: Symbol, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree]): ClassDef = + ClassDef(sym, Template(sym.info.parents map TypeTree, vparamss, argss, body)); + + /** Singleton object definition */ + case class ModuleDef(mods: Modifiers, name: Name, impl: Template) + extends ImplDef { + def keyword = "object"; + } + + def ModuleDef(sym: Symbol, impl: Template): ModuleDef = + posAssigner.atPos(sym.pos) { + ModuleDef(Modifiers(sym.flags), sym.name, impl) + } + + + abstract class ValOrDefDef extends MemberDef { + def tpt: Tree; + def rhs: Tree; + } + + /** Value definition */ + case class ValDef(mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) + extends ValOrDefDef { + assert(tpt.isType, tpt); + assert(rhs.isTerm, rhs); + def keyword = if (mods.isVariable) "var" else "val"; + override def namePos(source : SourceFile) = + if (pos == Position.NOPOS) Position.NOPOS; + else if (source.beginsWith(pos, "val ") || source.beginsWith(pos, "var ")) source.skipWhitespace(pos + ("val ").length()); + else if (source.content(pos) == ',') source.skipWhitespace(pos + 1); + else pos; + } + + + def ValDef(sym: Symbol, rhs: Tree): ValDef = { + posAssigner.atPos(sym.pos) { + ValDef(Modifiers(sym.flags), sym.name, TypeTree(sym.tpe), rhs) setSymbol sym + } + } + + def ValDef(sym: Symbol): ValDef = ValDef(sym, EmptyTree); + + /** Method definition */ + case class DefDef(mods: Modifiers, name: Name, tparams: List[AbsTypeDef], + vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) + extends ValOrDefDef { + assert(tpt.isType); + assert(rhs.isTerm); + def keyword = "def"; + } + + def DefDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef = + posAssigner.atPos(sym.pos) { + assert(sym != NoSymbol); + DefDef(Modifiers(sym.flags), + sym.name, + sym.typeParams map AbsTypeDef, + vparamss, + TypeTree(sym.tpe.finalResultType), + rhs) setSymbol sym + } + + def DefDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef = { + val vparamss = syntheticParams(sym, sym.tpe); + DefDef(sym, vparamss map (.map(ValDef)), rhs(vparamss)); + } + + /** Abstract type or type parameter */ + case class AbsTypeDef(mods: Modifiers, name: Name, lo: Tree, hi: Tree) + extends DefTree { + def keyword = ""; + + override def setPos(pos : Int) : this.type = { + val ret = super.setPos(pos); + ret; + } + def namePos = pos - name.length; + } + + def AbsTypeDef(sym: Symbol): AbsTypeDef = + posAssigner.atPos(sym.pos) { + AbsTypeDef(Modifiers(sym.flags), sym.name, + TypeTree(sym.info.bounds.lo), TypeTree(sym.info.bounds.hi)) + } + + /** Type alias */ + case class AliasTypeDef(mods: Modifiers, name: Name, tparams: List[AbsTypeDef], rhs: Tree) + extends MemberDef { + def keyword = "type"; + } + + def AliasTypeDef(sym: Symbol, rhs: Tree): AliasTypeDef = + posAssigner.atPos(sym.pos) { + AliasTypeDef(Modifiers(sym.flags), sym.name, sym.typeParams map AbsTypeDef, rhs) + } + + /** Labelled expression - the symbols in the array (must be Idents!) + * are those the label takes as argument + * + * The symbol that is given to the labeldef should have a MethodType + * (as if it were a nested function) + * + * jumps are apply nodes attributed with label symbol, the arguements + * will get assigned to the idents. + * + * note: on 2005-06-09 Martin, Iuli, Burak agreed to have forward + * jumps within a Block. + */ + case class LabelDef(name: Name, params: List[Ident], rhs: Tree) + extends DefTree with TermTree { + assert(rhs.isTerm); + } + + def LabelDef(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef = + posAssigner.atPos(sym.pos) { + LabelDef(sym.name, params map Ident, rhs) setSymbol sym + } + + /** Import clause */ + case class Import(expr: Tree, selectors: List[Pair[Name, Name]]) + extends SymTree; + + /** Attribuetd definition */ + case class Attributed(attribute: Tree, definition: Tree) + extends Tree { + override def symbol: Symbol = definition.symbol; + override def symbol_=(sym: Symbol): unit = { definition.symbol = sym } + } + + /** Documented definition, eliminated by analyzer */ + case class DocDef(comment: String, definition: Tree) + extends Tree { + override def symbol: Symbol = definition.symbol; + override def symbol_=(sym: Symbol): unit = { definition.symbol = sym } + } + + /** Instantiation template */ + case class Template(parents: List[Tree], body: List[Tree]) + extends SymTree { + // System.err.println("TEMPLATE: " + parents); + } + + def Template(parents: List[Tree], vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree]): Template = { + /** Add constructor to template */ + var vparamss1 = + vparamss map (.map (vd => + ValDef(Modifiers(vd.mods.flags & IMPLICIT | PARAM), vd.name, vd.tpt.duplicate, EmptyTree))); + if (vparamss1.isEmpty || + !vparamss1.head.isEmpty && (vparamss1.head.head.mods.flags & IMPLICIT) != 0) + vparamss1 = List() :: vparamss1; + val superRef: Tree = Select(Super(nme.EMPTY.toTypeName, nme.EMPTY.toTypeName), nme.CONSTRUCTOR); + val superCall = (superRef /: argss) (Apply); + val constr: Tree = DefDef(NoMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), superCall); + Template(parents, List.flatten(vparamss) ::: constr :: body) + } + + /** Block of expressions (semicolon separated expressions) */ + case class Block(stats: List[Tree], expr: Tree) + extends TermTree; + + /** Case clause in a pattern match, eliminated by TransMatch + * (except for occurences in switch statements) + */ + case class CaseDef(pat: Tree, guard: Tree, body: Tree) + extends Tree; + + /** casedef shorthand */ + def CaseDef(pat: Tree, body: Tree): CaseDef = CaseDef(pat, EmptyTree, body); + + /** Sequence of patterns (comma separated expressions), eliminated by TransMatch */ + case class Sequence(trees: List[Tree]) + extends TermTree; + + /** Alternatives of patterns, eliminated by TransMatch, except for + * occurences in encoded Switch stmt (=remaining Match(CaseDef(...)) + */ + case class Alternative(trees: List[Tree]) + extends TermTree; + + /** Repetition of pattern, eliminated by TransMatch */ + case class Star(elem: Tree) + extends TermTree; + + /** Bind of a variable to a rhs pattern, eliminated by TransMatch */ + case class Bind(name: Name, body: Tree) + extends DefTree; + + def Bind(sym: Symbol, body: Tree): Bind = + Bind(sym.name, body) setSymbol sym; + + /** Array of expressions, needs to be translated in backend, + */ + case class ArrayValue(elemtpt: Tree, elems: List[Tree]) + extends TermTree; + + /** Anonymous function, eliminated by analyzer */ + case class Function(vparams: List[ValDef], body: Tree) + extends TermTree with SymTree; + + /** Assignment */ + case class Assign(lhs: Tree, rhs: Tree) + extends TermTree; + + /** Conditional expression */ + case class If(cond: Tree, thenp: Tree, elsep: Tree) + extends TermTree; + + /** Pattern matching expression (before TransMatch) + * Switch statements (after TransMatch) + * + * after TM, cases will satisfy the following constraints: + * - all guards are EmptyTree, + * - all patterns will be either Literal(Constant(x:Int)) or Alternative(lit|...|lit) + * - except for an "otherwise" branch, which has pattern Ident(nme.WILDCARD) + */ + case class Match(selector: Tree, cases: List[CaseDef]) extends TermTree; + + /** Return expression */ + case class Return(expr: Tree) + extends TermTree with SymTree; + + case class Try(block: Tree, catches: List[CaseDef], finalizer: Tree) + extends TermTree; + + /** Throw expression */ + case class Throw(expr: Tree) + extends TermTree; + + /** Object instantiation + * @param tpt a class type + * one should always use factory method below to build a user level new. + */ + case class New(tpt: Tree) + extends TermTree { + assert(tpt.isType) + } + + /** Factory method for object creation <new tpt(args_1)...(args_n)> */ + def New(tpt: Tree, argss: List[List[Tree]]): Tree = { + assert(!argss.isEmpty); + val superRef: Tree = Select(New(tpt), nme.CONSTRUCTOR); + (superRef /: argss) (Apply); + } + + /** Type annotation, eliminated by explicit outer */ + case class Typed(expr: Tree, tpt: Tree) + extends TermTree; + + abstract class GenericApply(val fun0: Tree, val args0: List[Tree]) extends TermTree; + + + /** Type application */ + case class TypeApply(fun: Tree, args: List[Tree]) + extends GenericApply(fun, args) { + override def symbol: Symbol = fun.symbol; + override def symbol_=(sym: Symbol): unit = { fun.symbol = sym } + } + + /** Value application */ + case class Apply(fun: Tree, args: List[Tree]) + extends GenericApply(fun, args) { + override def symbol: Symbol = fun.symbol; + override def symbol_=(sym: Symbol): unit = { fun.symbol = sym } + } + + /** Super reference */ + case class Super(qual: Name, mixin: Name) + extends TermTree with SymTree; + + def Super(sym: Symbol, mixin: Name): Tree = Super(sym.name, mixin) setSymbol sym; + + /** Self reference */ + case class This(qual: Name) + extends TermTree with SymTree; + + def This(sym: Symbol): Tree = This(sym.name) setSymbol sym; + + /** Designator */ + case class Select(qualifier: Tree, selector: Name) + extends SymTree { + override def isTerm = selector.isTermName; + override def isType = selector.isTypeName; + + override def setPos(pos : Int) : this.type = { + val ret = super.setPos(pos); + // System.err.println("SELECT_POS: " + pos + " " + this); + // if (pos == 74) Thread.dumpStack(); + ret; + } + + + } + + def Select(qualifier: Tree, sym: Symbol): Select = + Select(qualifier, sym.name) setSymbol sym; + + /** Identifier */ + case class Ident(name: Name) + extends SymTree { + override def isTerm = name.isTermName; + override def isType = name.isTypeName; + + override def setPos(p : Int) : this.type = { + val ret = super.setPos(p); + ret; + } + + } + + def Ident(sym: Symbol): Ident = + Ident(sym.name) setSymbol sym; + + /** Literal */ + case class Literal(value: Constant) + extends TermTree { + assert(value != null) + } + + def Literal(value: Any): Literal = + Literal(Constant(value)); + + /** General type term, introduced by RefCheck. */ + case class TypeTree() extends TypTree { + var original : Tree = _; + + def setOriginal(tree : Tree) : this.type = { + tree match { + case tt : TypeTree => + System.err.println("Illegal: " + this + " to " + tree); + Thread.dumpStack(); + case _ => + } + + original = tree; + setPos(tree.pos); + } + override def setPos(pos : Int) : this.type = { + val ret = super.setPos(pos); + if (false && pos == 151 && original == null) { + System.err.println("TYPE: " + this + " POS=" + pos + " TPE=" + tpe); + Thread.dumpStack(); + } + ret; + } + + + override def isEmpty = tpe == null || tpe == NoType; + } + + def TypeTree(tp: Type): TypeTree = TypeTree() setType tp; + // def TypeTree(tp: Type, tree : Tree): TypeTree = TypeTree(tree) setType tp; + + + /** Singleton type, eliminated by RefCheck */ + case class SingletonTypeTree(ref: Tree) + extends TypTree; + + /** Type selection, eliminated by RefCheck */ + case class SelectFromTypeTree(qualifier: Tree, selector: Name) + extends TypTree with SymTree; + + /** Intersection type, eliminated by RefCheck */ + case class CompoundTypeTree(templ: Template) + extends TypTree; + + /** Applied type, eliminated by RefCheck */ + case class AppliedTypeTree(tpt: Tree, args: List[Tree]) + extends TypTree { + override def symbol: Symbol = tpt.symbol; + override def symbol_=(sym: Symbol): unit = { tpt.symbol = sym } + } + +/* A standard pattern match + case EmptyTree => + case PackageDef(name, stats) => + case ClassDef(mods, name, tparams, tpt, impl) => + case ModuleDef(mods, name, impl) => (eliminated by refcheck) + case ValDef(mods, name, tpt, rhs) => + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case AbsTypeDef(mods, name, lo, hi) => (eliminated by erasure) + case AliasTypeDef(mods, name, tparams, rhs) => (eliminated by erasure) + case LabelDef(name, params, rhs) => + case Import(expr, selectors) => (eliminated by typecheck) + case Attributed(attribute, definition) => (eliminated by typecheck) + case DocDef(comment, definition) => (eliminated by typecheck) + case Template(parents, body) => + case Block(stats, expr) => + case CaseDef(pat, guard, body) => (eliminated by transmatch) + case Sequence(trees) => (eliminated by transmatch) + case Alternative(trees) => (eliminated by transmatch) + case Star(elem) => (eliminated by transmatch) + case Bind(name, body) => (eliminated by transmatch) + case ArrayValue(elemtpt, trees) => (introduced by uncurry) + case Function(vparams, body) => (eliminated by typecheck) + case Assign(lhs, rhs) => + case If(cond, thenp, elsep) => + case Match(selector, cases) => + case Return(expr) => + case Try(block, catches, finalizer) => + case Throw(expr) => + case New(tpt) => + case Typed(expr, tpt) => (eliminated by erasure) + case TypeApply(fun, args) => + case Apply(fun, args) => + case Super(qual, mixin) => + case This(qual) => + case Select(qualifier, selector) => + case Ident(name) => + case Literal(value) => + case TypeTree() => + case SingletonTypeTree(ref) => (eliminated by typecheck) + case SelectFromTypeTree(qualifier, selector) => (eliminated by typecheck) + case CompoundTypeTree(templ: Template) => (eliminated by typecheck) + case AppliedTypeTree(tpt, args) => (eliminated by typecheck) +*/ + + trait TreeCopier { + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], tpt: Tree, impl: Template): ClassDef; + def PackageDef(tree: Tree, name: Name, stats: List[Tree]): PackageDef; + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template): ModuleDef; + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree): ValDef; + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef; + def AbsTypeDef(tree: Tree, mods: Modifiers, name: Name, lo: Tree, hi: Tree): AbsTypeDef; + def AliasTypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], rhs: Tree): AliasTypeDef; + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree): LabelDef; + def Import(tree: Tree, expr: Tree, selectors: List[Pair[Name, Name]]): Import; + def Attributed(tree: Tree, attribute: Tree, definition: Tree): Attributed; + def DocDef(tree: Tree, comment: String, definition: Tree): DocDef; + def Template(tree: Tree, parents: List[Tree], body: List[Tree]): Template; + def Block(tree: Tree, stats: List[Tree], expr: Tree): Block; + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree): CaseDef; + def Sequence(tree: Tree, trees: List[Tree]): Sequence; + def Alternative(tree: Tree, trees: List[Tree]): Alternative; + def Star(tree: Tree, elem: Tree): Star; + def Bind(tree: Tree, name: Name, body: Tree): Bind; + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]): ArrayValue; + def Function(tree: Tree, vparams: List[ValDef], body: Tree): Function; + def Assign(tree: Tree, lhs: Tree, rhs: Tree): Assign; + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree): If; + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]): Match; + def Return(tree: Tree, expr: Tree): Return; + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree): Try; + def Throw(tree: Tree, expr: Tree): Throw; + def New(tree: Tree, tpt: Tree): New; + def Typed(tree: Tree, expr: Tree, tpt: Tree): Typed; + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]): TypeApply; + def Apply(tree: Tree, fun: Tree, args: List[Tree]): Apply; + def Super(tree: Tree, qual: Name, mixin: Name): Super; + def This(tree: Tree, qual: Name): This; + def Select(tree: Tree, qualifier: Tree, selector: Name): Select; + def Ident(tree: Tree, name: Name): Ident; + def Literal(tree: Tree, value: Constant): Literal; + def TypeTree(tree: Tree): TypeTree; + def SingletonTypeTree(tree: Tree, ref: Tree): SingletonTypeTree; + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name): SelectFromTypeTree; + def CompoundTypeTree(tree: Tree, templ: Template): CompoundTypeTree; + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]): AppliedTypeTree; + } + + class StrictTreeCopier extends TreeCopier { + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], tpt: Tree, impl: Template) = + new ClassDef(mods, name, tparams, tpt, impl).copyAttrs(tree); + def PackageDef(tree: Tree, name: Name, stats: List[Tree]) = + new PackageDef(name, stats).copyAttrs(tree); + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template) = + new ModuleDef(mods, name, impl).copyAttrs(tree); + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) = + new ValDef(mods, name, tpt, rhs).copyAttrs(tree); + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) = + new DefDef(mods, name, tparams, vparamss, tpt, rhs).copyAttrs(tree); + def AbsTypeDef(tree: Tree, mods: Modifiers, name: Name, lo: Tree, hi: Tree) = + new AbsTypeDef(mods, name, lo, hi).copyAttrs(tree); + def AliasTypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], rhs: Tree) = + new AliasTypeDef(mods, name, tparams, rhs).copyAttrs(tree); + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree) = + new LabelDef(name, params, rhs).copyAttrs(tree); + def Import(tree: Tree, expr: Tree, selectors: List[Pair[Name, Name]]) = + new Import(expr, selectors).copyAttrs(tree); + def Attributed(tree: Tree, attribute: Tree, definition: Tree) = + new Attributed(attribute, definition).copyAttrs(tree); + def DocDef(tree: Tree, comment: String, definition: Tree) = + new DocDef(comment, definition).copyAttrs(tree); + def Template(tree: Tree, parents: List[Tree], body: List[Tree]) = + new Template(parents, body).copyAttrs(tree); + def Block(tree: Tree, stats: List[Tree], expr: Tree) = + new Block(stats, expr).copyAttrs(tree); + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree) = + new CaseDef(pat, guard, body).copyAttrs(tree); + def Sequence(tree: Tree, trees: List[Tree]) = + new Sequence(trees).copyAttrs(tree); + def Alternative(tree: Tree, trees: List[Tree]) = + new Alternative(trees).copyAttrs(tree); + def Star(tree: Tree, elem: Tree) = + new Star(elem).copyAttrs(tree); + def Bind(tree: Tree, name: Name, body: Tree) = + new Bind(name, body).copyAttrs(tree); + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]) = + new ArrayValue(elemtpt, trees).copyAttrs(tree); + def Function(tree: Tree, vparams: List[ValDef], body: Tree) = + new Function(vparams, body).copyAttrs(tree); + def Assign(tree: Tree, lhs: Tree, rhs: Tree) = + new Assign(lhs, rhs).copyAttrs(tree); + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree) = + new If(cond, thenp, elsep).copyAttrs(tree); + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]) = + new Match(selector, cases).copyAttrs(tree); + def Return(tree: Tree, expr: Tree) = + new Return(expr).copyAttrs(tree); + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree) = + new Try(block, catches, finalizer).copyAttrs(tree); + def Throw(tree: Tree, expr: Tree) = + new Throw(expr).copyAttrs(tree); + def New(tree: Tree, tpt: Tree) = + new New(tpt).copyAttrs(tree); + def Typed(tree: Tree, expr: Tree, tpt: Tree) = + new Typed(expr, tpt).copyAttrs(tree); + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]) = + new TypeApply(fun, args).copyAttrs(tree); + def Apply(tree: Tree, fun: Tree, args: List[Tree]) = + new Apply(fun, args).copyAttrs(tree); + def Super(tree: Tree, qual: Name, mixin: Name) = + new Super(qual, mixin).copyAttrs(tree); + def This(tree: Tree, qual: Name) = + new This(qual).copyAttrs(tree); + def Select(tree: Tree, qualifier: Tree, selector: Name) = + new Select(qualifier, selector).copyAttrs(tree); + def Ident(tree: Tree, name: Name) = + new Ident(name).copyAttrs(tree); + def Literal(tree: Tree, value: Constant) = + new Literal(value).copyAttrs(tree); + def TypeTree(tree: Tree) = + new TypeTree().copyAttrs(tree); + def SingletonTypeTree(tree: Tree, ref: Tree) = + new SingletonTypeTree(ref).copyAttrs(tree); + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name) = + new SelectFromTypeTree(qualifier, selector).copyAttrs(tree); + def CompoundTypeTree(tree: Tree, templ: Template) = + new CompoundTypeTree(templ).copyAttrs(tree); + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]) = + new AppliedTypeTree(tpt, args).copyAttrs(tree) + } + + class LazyTreeCopier(copy: TreeCopier) extends TreeCopier { + def this() = this(new StrictTreeCopier); + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], tpt: Tree, impl: Template) = tree match { + case t @ ClassDef(mods0, name0, tparams0, tpt0, impl0) + if (mods0 == mods && (name0 == name) && (tparams0 == tparams) && (tpt0 == tpt) && (impl0 == impl)) => t + case _ => copy.ClassDef(tree, mods, name, tparams, tpt, impl) + } + def PackageDef(tree: Tree, name: Name, stats: List[Tree]) = tree match { + case t @ PackageDef(name0, stats0) + if ((name0 == name) && (stats0 == stats)) => t + case _ => copy.PackageDef(tree, name, stats) + } + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template) = tree match { + case t @ ModuleDef(mods0, name0, impl0) + if (mods0 == mods && (name0 == name) && (impl0 == impl)) => t + case _ => copy.ModuleDef(tree, mods, name, impl) + } + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) = tree match { + case t @ ValDef(mods0, name0, tpt0, rhs0) + if (mods0 == mods && (name0 == name) && (tpt0 == tpt) && (rhs0 == rhs)) => t + case _ => copy.ValDef(tree, mods, name, tpt, rhs) + } + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) = tree match { + case t @ DefDef(mods0, name0, tparams0, vparamss0, tpt0, rhs0) + if (mods0 == mods && (name0 == name) && (tparams0 == tparams) && (vparamss0 == vparamss) && (tpt0 == tpt) && (rhs == rhs0)) => t + case _ => copy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs) + } + def AbsTypeDef(tree: Tree, mods: Modifiers, name: Name, lo: Tree, hi: Tree) = tree match { + case t @ AbsTypeDef(mods0, name0, lo0, hi0) + if (mods0 == mods && (name0 == name) && (lo0 == lo) && (hi0 == hi)) => t + case _ => copy.AbsTypeDef(tree, mods, name, lo, hi) + } + def AliasTypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], rhs: Tree) = tree match { + case t @ AliasTypeDef(mods0, name0, tparams0, rhs0) + if (mods0 == mods && (name0 == name) && (tparams0 == tparams) && (rhs0 == rhs)) => t + case _ => copy.AliasTypeDef(tree, mods, name, tparams, rhs) + } + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree) = tree match { + case t @ LabelDef(name0, params0, rhs0) + if ((name0 == name) && (params0 == params) && (rhs0 == rhs)) => t + case _ => copy.LabelDef(tree, name, params, rhs) + } + def Import(tree: Tree, expr: Tree, selectors: List[Pair[Name, Name]]) = tree match { + case t @ Import(expr0, selectors0) + if ((expr0 == expr) && (selectors0 == selectors)) => t + case _ => copy.Import(tree, expr, selectors) + } + def Attributed(tree: Tree, attribute: Tree, definition: Tree) = tree match { + case t @ Attributed(attribute0, definition0) + if ((attribute0 == attribute) && (definition0 == definition)) => t + case _ => copy.Attributed(tree, attribute, definition) + } + def DocDef(tree: Tree, comment: String, definition: Tree) = tree match { + case t @ DocDef(comment0, definition0) + if ((comment0 == comment) && (definition0 == definition)) => t + case _ => copy.DocDef(tree, comment, definition) + } + def Template(tree: Tree, parents: List[Tree], body: List[Tree]) = tree match { + case t @ Template(parents0, body0) + if ((parents0 == parents) && (body0 == body)) => t + case _ => copy.Template(tree, parents, body) + } + def Block(tree: Tree, stats: List[Tree], expr: Tree) = tree match { + case t @ Block(stats0, expr0) + if ((stats0 == stats) && (expr0 == expr)) => t + case _ => copy.Block(tree, stats, expr) + } + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree) = tree match { + case t @ CaseDef(pat0, guard0, body0) + if ((pat0 == pat) && (guard0 == guard) && (body0 == body)) => t + case _ => copy.CaseDef(tree, pat, guard, body) + } + def Sequence(tree: Tree, trees: List[Tree]) = tree match { + case t @ Sequence(trees0) + if ((trees0 == trees)) => t + case _ => copy.Sequence(tree, trees) + } + def Alternative(tree: Tree, trees: List[Tree]) = tree match { + case t @ Alternative(trees0) + if ((trees0 == trees)) => t + case _ => copy.Alternative(tree, trees) + } + def Star(tree: Tree, elem: Tree) = tree match { + case t @ Star(elem0) + if ((elem0 == elem)) => t + case _ => copy.Star(tree, elem) + } + def Bind(tree: Tree, name: Name, body: Tree) = tree match { + case t @ Bind(name0, body0) + if ((name0 == name) && (body0 == body)) => t + case _ => copy.Bind(tree, name, body) + } + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]) = tree match { + case t @ ArrayValue(elemtpt0, trees0) + if ((elemtpt0 == elemtpt) && (trees0 == trees)) => t + case _ => copy.ArrayValue(tree, elemtpt, trees) + } + def Function(tree: Tree, vparams: List[ValDef], body: Tree) = tree match { + case t @ Function(vparams0, body0) + if ((vparams0 == vparams) && (body0 == body)) => t + case _ => copy.Function(tree, vparams, body) + } + def Assign(tree: Tree, lhs: Tree, rhs: Tree) = tree match { + case t @ Assign(lhs0, rhs0) + if ((lhs0 == lhs) && (rhs0 == rhs)) => t + case _ => copy.Assign(tree, lhs, rhs) + } + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree) = tree match { + case t @ If(cond0, thenp0, elsep0) + if ((cond0 == cond) && (thenp0 == thenp) && (elsep0 == elsep)) => t + case _ => copy.If(tree, cond, thenp, elsep) + } + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]) = tree match { + case t @ Match(selector0, cases0) + if ((selector0 == selector) && (cases0 == cases)) => t + case _ => copy.Match(tree, selector, cases) + } + def Return(tree: Tree, expr: Tree) = tree match { + case t @ Return(expr0) + if ((expr0 == expr)) => t + case _ => copy.Return(tree, expr) + } + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree) = tree match { + case t @ Try(block0, catches0, finalizer0) + if ((block0 == block) && (catches0 == catches) && (finalizer0 == finalizer)) => t + case _ => copy.Try(tree, block, catches, finalizer) + } + def Throw(tree: Tree, expr: Tree) = tree match { + case t @ Throw(expr0) + if ((expr0 == expr)) => t + case _ => copy.Throw(tree, expr) + } + def New(tree: Tree, tpt: Tree) = tree match { + case t @ New(tpt0) + if ((tpt0 == tpt)) => t + case _ => copy.New(tree, tpt) + } + def Typed(tree: Tree, expr: Tree, tpt: Tree) = tree match { + case t @ Typed(expr0, tpt0) + if ((expr0 == expr) && (tpt0 == tpt)) => t + case _ => copy.Typed(tree, expr, tpt) + } + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { + case t @ TypeApply(fun0, args0) + if ((fun0 == fun) && (args0 == args)) => t + case _ => copy.TypeApply(tree, fun, args) + } + def Apply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { + case t @ Apply(fun0, args0) + if ((fun0 == fun) && (args0 == args)) => t + case _ => copy.Apply(tree, fun, args) + } + def Super(tree: Tree, qual: Name, mixin: Name) = tree match { + case t @ Super(qual0, mixin0) + if ((qual0 == qual) && (mixin0 == mixin)) => t + case _ => copy.Super(tree, qual, mixin) + } + def This(tree: Tree, qual: Name) = tree match { + case t @ This(qual0) + if ((qual0 == qual)) => t + case _ => copy.This(tree, qual) + } + def Select(tree: Tree, qualifier: Tree, selector: Name) = tree match { + case t @ Select(qualifier0, selector0) + if ((qualifier0 == qualifier) && (selector0 == selector)) => t + case _ => copy.Select(tree, qualifier, selector) + } + def Ident(tree: Tree, name: Name) = tree match { + case t @ Ident(name0) + if ((name0 == name)) => t + case _ => copy.Ident(tree, name) + } + def Literal(tree: Tree, value: Constant) = tree match { + case t @ Literal(value0) + if (value0 == value) => t + case _ => copy.Literal(tree, value) + } + def TypeTree(tree: Tree) = tree match { + case t @ TypeTree() => t + case _ => copy.TypeTree(tree) + } + def SingletonTypeTree(tree: Tree, ref: Tree) = tree match { + case t @ SingletonTypeTree(ref0) + if ((ref0 == ref)) => t + case _ => copy.SingletonTypeTree(tree, ref) + } + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name) = tree match { + case t @ SelectFromTypeTree(qualifier0, selector0) + if ((qualifier0 == qualifier) && (selector0 == selector)) => t + case _ => copy.SelectFromTypeTree(tree, qualifier, selector) + } + def CompoundTypeTree(tree: Tree, templ: Template) = tree match { + case t @ CompoundTypeTree(templ0) + if (templ0 == templ) => t + case _ => copy.CompoundTypeTree(tree, templ) + } + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]) = tree match { + case t @ AppliedTypeTree(tpt0, args0) + if ((tpt0 == tpt) && (args0 == args)) => t + case _ => copy.AppliedTypeTree(tree, tpt, args) + } + } + + abstract class Transformer { + val copy: TreeCopier = new LazyTreeCopier; + protected var currentOwner: Symbol = definitions.RootClass; + def transform(tree: Tree): Tree = tree match { + case EmptyTree => + tree + case PackageDef(name, stats) => + atOwner(tree.symbol.moduleClass) { + copy.PackageDef(tree, name, transformStats(stats, currentOwner)) + } + case ClassDef(mods, name, tparams, tpt, impl) => + atOwner(tree.symbol) { + copy.ClassDef(tree, mods, name, transformAbsTypeDefs(tparams), transform(tpt), transformTemplate(impl)) + } + case ModuleDef(mods, name, impl) => + atOwner(tree.symbol.moduleClass) { + copy.ModuleDef(tree, mods, name, transformTemplate(impl)) + } + case ValDef(mods, name, tpt, rhs) => + atOwner(tree.symbol) { + copy.ValDef(tree, mods, name, transform(tpt), transform(rhs)) + } + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + atOwner(tree.symbol) { + copy.DefDef( + tree, mods, name, transformAbsTypeDefs(tparams), transformValDefss(vparamss), transform(tpt), transform(rhs)) + } + case AbsTypeDef(mods, name, lo, hi) => + atOwner(tree.symbol) { + copy.AbsTypeDef(tree, mods, name, transform(lo), transform(hi)) + } + case AliasTypeDef(mods, name, tparams, rhs) => + atOwner(tree.symbol) { + copy.AliasTypeDef(tree, mods, name, transformAbsTypeDefs(tparams), transform(rhs)) + } + case LabelDef(name, params, rhs) => + copy.LabelDef(tree, name, transformIdents(params), transform(rhs)) //bq: Martin, once, atOwner(...) works, also change `LamdaLifter.proxy' + case Import(expr, selectors) => + copy.Import(tree, transform(expr), selectors) + case Attributed(attribute, definition) => + copy.Attributed(tree, transform(attribute), transform(definition)) + case DocDef(comment, definition) => + copy.DocDef(tree, comment, transform(definition)) + case Template(parents, body) => + copy.Template(tree, transformTrees(parents), transformStats(body, tree.symbol)) + case Block(stats, expr) => + copy.Block(tree, transformStats(stats, currentOwner), transform(expr)) + case CaseDef(pat, guard, body) => + copy.CaseDef(tree, transform(pat), transform(guard), transform(body)) + case Sequence(trees) => + copy.Sequence(tree, transformTrees(trees)) + case Alternative(trees) => + copy.Alternative(tree, transformTrees(trees)) + case Star(elem) => + copy.Star(tree, transform(elem)) + case Bind(name, body) => + copy.Bind(tree, name, transform(body)) + case ArrayValue(elemtpt, trees) => + copy.ArrayValue(tree, transform(elemtpt), transformTrees(trees)) + case Function(vparams, body) => + copy.Function(tree, transformValDefs(vparams), transform(body)) + case Assign(lhs, rhs) => + copy.Assign(tree, transform(lhs), transform(rhs)) + case If(cond, thenp, elsep) => + copy.If(tree, transform(cond), transform(thenp), transform(elsep)) + case Match(selector, cases) => + copy.Match(tree, transform(selector), transformCaseDefs(cases)) + case Return(expr) => + copy.Return(tree, transform(expr)) + case Try(block, catches, finalizer) => + copy.Try(tree, transform(block), transformCaseDefs(catches), transform(finalizer)) + case Throw(expr) => + copy.Throw(tree, transform(expr)) + case New(tpt) => + copy.New(tree, transform(tpt)) + case Typed(expr, tpt) => + copy.Typed(tree, transform(expr), transform(tpt)) + case TypeApply(fun, args) => + copy.TypeApply(tree, transform(fun), transformTrees(args)) + case Apply(fun, args) => + copy.Apply(tree, transform(fun), transformTrees(args)) + case Super(qual, mixin) => + copy.Super(tree, qual, mixin) + case This(qual) => + copy.This(tree, qual) + case Select(qualifier, selector) => + copy.Select(tree, transform(qualifier), selector) + case Ident(name) => + copy.Ident(tree, name) + case Literal(value) => + copy.Literal(tree, value) + case TypeTree() => + copy.TypeTree(tree) + case SingletonTypeTree(ref) => + copy.SingletonTypeTree(tree, transform(ref)) + case SelectFromTypeTree(qualifier, selector) => + copy.SelectFromTypeTree(tree, transform(qualifier), selector) + case CompoundTypeTree(templ) => + copy.CompoundTypeTree(tree, transformTemplate(templ)) + case AppliedTypeTree(tpt, args) => + copy.AppliedTypeTree(tree, transform(tpt), transformTrees(args)) + } + + def transformTrees(trees: List[Tree]): List[Tree] = + List.mapConserve(trees)(transform); + def transformTemplate(tree: Template): Template = + transform(tree: Tree).asInstanceOf[Template]; + def transformAbsTypeDefs(trees: List[AbsTypeDef]): List[AbsTypeDef] = + List.mapConserve(trees)(tree => transform(tree).asInstanceOf[AbsTypeDef]); + def transformValDefs(trees: List[ValDef]): List[ValDef] = + List.mapConserve(trees)(tree => transform(tree).asInstanceOf[ValDef]); + def transformValDefss(treess: List[List[ValDef]]): List[List[ValDef]] = + List.mapConserve(treess)(tree => transformValDefs(tree)); + def transformCaseDefs(trees: List[CaseDef]): List[CaseDef] = + List.mapConserve(trees)(tree => transform(tree).asInstanceOf[CaseDef]); + def transformIdents(trees: List[Ident]): List[Ident] = + List.mapConserve(trees)(tree => transform(tree).asInstanceOf[Ident]); + def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = + List.mapConserve(stats)(stat => + if (exprOwner != currentOwner && stat.isTerm) atOwner(exprOwner)(transform(stat)) + else transform(stat)) filter (EmptyTree !=); + def transformUnit(unit: CompilationUnit): unit = { unit.body = transform(unit.body) } + + def atOwner[A](owner: Symbol)(trans: => A): A = { + val prevOwner = currentOwner; + currentOwner = owner; + val result = trans; + currentOwner = prevOwner; + result + } + } + + class Traverser { + protected var currentOwner: Symbol = definitions.RootClass; + def traverse(tree: Tree): unit = tree match { + case EmptyTree => + ; + case PackageDef(name, stats) => + atOwner(tree.symbol.moduleClass) { + traverseTrees(stats) + } + case ClassDef(mods, name, tparams, tpt, impl) => + atOwner(tree.symbol) { + traverseTrees(tparams); traverse(tpt); traverse(impl) + } + case ModuleDef(mods, name, impl) => + atOwner(tree.symbol.moduleClass) { + traverse(impl) + } + case ValDef(mods, name, tpt, rhs) => + atOwner(tree.symbol) { + traverse(tpt); traverse(rhs) + } + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + atOwner(tree.symbol) { + traverseTrees(tparams); traverseTreess(vparamss); traverse(tpt); traverse(rhs) + } + case AbsTypeDef(mods, name, lo, hi) => + atOwner(tree.symbol) { + traverse(lo); traverse(hi); + } + case AliasTypeDef(mods, name, tparams, rhs) => + atOwner(tree.symbol) { + traverseTrees(tparams); traverse(rhs) + } + case LabelDef(name, params, rhs) => + traverseTrees(params); traverse(rhs) + case Import(expr, selectors) => + traverse(expr) + case Attributed(attribute, definition) => + traverse(attribute); traverse(definition) + case DocDef(comment, definition) => + traverse(definition) + case Template(parents, body) => + traverseTrees(parents); traverseStats(body, tree.symbol) + case Block(stats, expr) => + traverseTrees(stats); traverse(expr) + case CaseDef(pat, guard, body) => + traverse(pat); traverse(guard); traverse(body) + case Sequence(trees) => + traverseTrees(trees) + case Alternative(trees) => + traverseTrees(trees) + case Star(elem) => + traverse(elem) + case Bind(name, body) => + traverse(body) + case ArrayValue(elemtpt, trees) => + traverse(elemtpt); traverseTrees(trees) + case Function(vparams, body) => + traverseTrees(vparams); traverse(body) + case Assign(lhs, rhs) => + traverse(lhs); traverse(rhs) + case If(cond, thenp, elsep) => + traverse(cond); traverse(thenp); traverse(elsep) + case Match(selector, cases) => + traverse(selector); traverseTrees(cases) + case Return(expr) => + traverse(expr) + case Try(block, catches, finalizer) => + traverse(block); traverseTrees(catches); traverse(finalizer) + case Throw(expr) => + traverse(expr) + case New(tpt) => + traverse(tpt) + case Typed(expr, tpt) => + traverse(expr); traverse(tpt) + case TypeApply(fun, args) => + traverse(fun); traverseTrees(args) + case Apply(fun, args) => + traverse(fun); traverseTrees(args) + case Super(_, _) => + ; + case This(_) => + ; + case Select(qualifier, selector) => + traverse(qualifier) + case Ident(_) => + ; + case Literal(_) => + ; + case TypeTree() => + ; + case SingletonTypeTree(ref) => + traverse(ref) + case SelectFromTypeTree(qualifier, selector) => + traverse(qualifier) + case CompoundTypeTree(templ) => + traverse(templ) + case AppliedTypeTree(tpt, args) => + traverse(tpt); traverseTrees(args) + } + + def traverseTrees(trees: List[Tree]): unit = + trees foreach traverse; + def traverseTreess(treess: List[List[Tree]]): unit = + treess foreach traverseTrees; + def traverseStats(stats: List[Tree], exprOwner: Symbol): unit = + stats foreach (stat => + if (exprOwner != currentOwner && stat.isTerm) atOwner(exprOwner)(traverse(stat)) + else traverse(stat)); + def apply[T <: Tree](tree: T): T = { traverse(tree); tree } + + def atOwner(owner: Symbol)(traverse: => unit): unit = { + val prevOwner = currentOwner; + currentOwner = owner; + traverse; + currentOwner = prevOwner; + } + } + + class TreeSubstituter(from: List[Symbol], to: List[Tree]) extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Ident(_) => + def subst(from: List[Symbol], to: List[Tree]): Tree = + if (from.isEmpty) tree + else if (tree.symbol == from.head) to.head + else subst(from.tail, to.tail); + subst(from, to) + case _ => + super.transform(tree) + } + } + + class TreeTypeSubstituter(from: List[Symbol], to: List[Type]) extends Traverser { + val typeSubst = new SubstTypeMap(from, to); + override def traverse(tree: Tree): unit = { + if (tree.tpe != null) tree.tpe = typeSubst(tree.tpe); + super.traverse(tree) + } + override def apply[T <: Tree](tree: T): T = super.apply(tree.duplicate) + } + + class TreeSymSubstituter(from: List[Symbol], to: List[Symbol]) extends Traverser { + val symSubst = new SubstSymMap(from, to); + override def traverse(tree: Tree): unit = { + def subst(from: List[Symbol], to: List[Symbol]): unit = { + if (!from.isEmpty) + if (tree.symbol == from.head) tree setSymbol to.head + else subst(from.tail, to.tail) + } + if (tree.tpe != null) tree.tpe = symSubst(tree.tpe); + if (tree.hasSymbol) subst(from, to); + super.traverse(tree) + } + override def apply[T <: Tree](tree: T): T = super.apply(tree.duplicate) + } + + class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser { + override def traverse(tree: Tree): unit = { + if ((tree.isDef || tree.isInstanceOf[Function]) && tree.symbol != NoSymbol && tree.symbol.owner == oldowner) + tree.symbol.owner = newowner; + super.traverse(tree) + } + } + + final class TreeList { + private var trees = List[Tree](); + def append(t: Tree): TreeList = { trees = t :: trees; this } + def append(ts: List[Tree]): TreeList = { trees = ts reverse_::: trees; this } + def toList: List[Tree] = trees.reverse; + } + + object posAssigner extends Traverser { + private var pos: int = _; + override def traverse(t: Tree): unit = + if (t != EmptyTree && t.pos == Position.NOPOS) { + t.setPos(pos); + super.traverse(t); + } + def atPos[T <: Tree](pos: int)(tree: T): T = { + this.pos = pos; + traverse(tree); + tree + } + } +} + diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala new file mode 100644 index 0000000000..cf4244cb3f --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala @@ -0,0 +1,583 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +//import java.lang.{Integer, Long, Float, Double}; + +//import scalac._; +//import scalac.ast._; +//import scalac.atree.AConstant; +//import scalac.symtab.Modifiers; + +import scala.Iterator; +import scala.collection.immutable.ListMap ; +import scala.collection.mutable; +//import scala.tools.scalac.util.NewArray; +import scala.tools.nsc.util.Position; +import scala.xml.{Text,TextBuffer}; + + +trait MarkupParsers: SyntaxAnalyzer { + + import global._ ; + import posAssigner.atPos; + +class MarkupParser(unit: CompilationUnit, s: Scanner, p: Parser, presWS: boolean) /*with scala.xml.parsing.MarkupParser[Tree,Tree] */{ + + import Tokens.{EMPTY, LBRACE, RBRACE}; + + final val preserveWS = presWS; + + import p.{symbXMLBuilder => handle}; + import s.token; + + /** the XML tree factory */ + //final val handle: SymbolicXMLBuilder = p.symbXMLBuilder; + //new SymbolicXMLBuilder(unit.global.make, unit.global.treeGen, p, presWS); + + /** holds the position in the source file */ + /*[Duplicate]*/ var pos: Int = _; + + /** holds temporary values of pos */ + /*[Duplicate]*/ var tmppos: Int = _; + + /** holds the next character */ + /*[Duplicate]*/ var ch: Char = _; + + /** character buffer, for names */ + /*[Duplicate]*/ protected val cbuf = new StringBuffer(); + + /** append Unicode character to name buffer*/ + /*[Duplicate]*/ protected def putChar(c: char) = cbuf.append( c ); + + /*[Duplicate]*/ var xEmbeddedBlock = false; + + /** munch expected XML token, report syntax error for unexpected */ + /*[Duplicate]*/ def xToken(that: Char): Unit = { + if( ch == that ) + nextch; + else + reportSyntaxError("'" + that + "' expected instead of '" + ch + "'"); + } + + var debugLastStartElement = new mutable.Stack[Pair[Int,String]]; + + /** checks whether next character starts a Scala block, if yes, skip it. + * @return true if next character starts a scala block + */ + /*[Duplicate]*/ def xCheckEmbeddedBlock:Boolean = { + xEmbeddedBlock = + enableEmbeddedExpressions && ( ch == '{' ) && { nextch;( ch != '{' ) }; + return xEmbeddedBlock; + } + + /** parse attribute and add it to listmap + * [41] Attributes ::= { S Name Eq AttValue } + * AttValue ::= `'` { _ } `'` + * | `"` { _ } `"` + * | `{` scalablock `}` + */ + /*[Duplicate]*/ def xAttributes = { + var aMap = new mutable.HashMap[String, Tree](); + while (xml.Parsing.isNameStart(ch)) { + val key = xName; + xEQ; + val delim = ch; + val pos1 = pos; + val value: /* AttribValue[*/Tree/*]*/ = ch match { + case '"' | '\'' => + nextch; + val tmp = xAttributeValue( delim ); + nextch; + Literal(Constant(tmp)); + case '{' if enableEmbeddedExpressions => + nextch; + xEmbeddedExpr; + case _ => + reportSyntaxError( "' or \" delimited attribute value or '{' scala-expr '}' expected" ); + Literal(Constant("<syntax-error>")) + }; + // well-formedness constraint: unique attribute names + if( aMap.contains( key )) + reportSyntaxError( "attribute "+key+" may only be defined once" ); + aMap.update( key, value ); + if(( ch != '/' )&&( ch != '>' )) + xSpace; + }; + aMap + } + + /** attribute value, terminated by either ' or ". value may not contain <. + * @param endch either ' or " + */ + /*[Duplicate]*/ def xAttributeValue(endCh: char): String = { + while (ch != endCh) { + putChar(ch); + nextch; + }; + val str = cbuf.toString(); + cbuf.setLength(0); + // @todo: normalize attribute value + // well-formedness constraint + if (str.indexOf('<') != -1) { + reportSyntaxError( "'<' not allowed in attrib value" ); "" + } else { + str + } + } + + /** parse a start or empty tag. + * [40] STag ::= '<' Name { S Attribute } [S] + * [44] EmptyElemTag ::= '<' Name { S Attribute } [S] + */ + /*[Duplicate]*/ def xTag: Pair[String, mutable.Map[String, Tree]] = { + val elemName = xName; + xSpaceOpt; + val aMap = if (xml.Parsing.isNameStart(ch)) { + xAttributes; + } else { + new mutable.HashMap[String, Tree](); + } + Tuple2( elemName, aMap ); + } + + /* [42] '<' xmlEndTag ::= '<' '/' Name S? '>' */ + /*[Duplicate]*/ def xEndTag(n: String) = { + xToken('/'); + val m = xName; + if(n != m) reportSyntaxError( "expected closing tag of " + n/* +", not "+m*/); + xSpaceOpt; + xToken('>') + } + + /** '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>' + * + * see [15] + */ + /*[Duplicate]*/ def xCharData: Tree = { + xToken('['); + xToken('C'); + xToken('D'); + xToken('A'); + xToken('T'); + xToken('A'); + xToken('['); + val pos1 = pos; + val sb:StringBuffer = new StringBuffer(); + while (true) { + if (ch==']' && + { sb.append( ch ); nextch; ch == ']' } && + { sb.append( ch ); nextch; ch == '>' } ) { + sb.setLength( sb.length() - 2 ); + nextch; + return handle.charData( pos1, sb.toString() ); + } else sb.append( ch ); + nextch; + } + Predef.error("this cannot happen"); + }; + + /** CharRef ::= "&#" '0'..'9' {'0'..'9'} ";" + * | "&#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";" + * + * see [66] + */ + /*[Duplicate]*/ def xCharRef:String = { + val hex = ( ch == 'x' ) && { nextch; true }; + 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 + Character.digit( ch, base ); + 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 + Character.digit( ch, base ); + case _ => + reportSyntaxError("character '"+ch+" not allowed in char ref\n"); + } + nextch; + } + new String(Predef.Array(i.asInstanceOf[char])) + } +/** Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' + * + * see [15] + */ + /*[Duplicate]*/ def xComment: Tree = { + val sb:StringBuffer = new StringBuffer(); + xToken('-'); + xToken('-'); + while (true) { + if( ch=='-' && { sb.append( ch ); nextch; ch == '-' } ) { + sb.setLength( sb.length() - 1 ); + nextch; + xToken('>'); + return handle.comment( pos, sb.toString() ); + } else sb.append( ch ); + nextch; + } + Predef.error("this cannot happen"); + }; + + /*[Duplicate]*/ def appendText(pos: int, ts:mutable.Buffer[Tree], txt:String):Unit = { + if( !preserveWS ) + for( val t <- TextBuffer.fromString( txt ).toText ) { + ts.append( handle.text( pos, t.text ) ); + } + else + ts.append( handle.text( pos, txt )); + } + + /*[Duplicate]*/ def content: mutable.Buffer[Tree] = { + var ts = new mutable.ArrayBuffer[Tree]; + var exit = false; + while( !exit ) { + if( xEmbeddedBlock ) { + ts.append( xEmbeddedExpr ); + } else { + tmppos = pos; + ch match { + case '<' => // another tag + nextch; + ch match { + case '/' => + exit = true; // end tag + case '!' => + nextch; + if( '[' == ch ) // CDATA + ts.append( xCharData ); + else // comment + ts.append( xComment ); + case '?' => // PI + nextch; + ts.append( xProcInstr ); + case _ => + ts.append( element ); // child + } + + case '{' => + if( xCheckEmbeddedBlock ) { + ts.append(xEmbeddedExpr); + } else { + val str = new StringBuffer("{"); + str.append( xText ); + appendText(tmppos, ts, str.toString()); + } + // postcond: xEmbeddedBlock == false! + case '&' => // EntityRef or CharRef + nextch; + ch match { + case '#' => // CharacterRef + nextch; + val theChar = handle.text( tmppos, xCharRef ); + xToken(';'); + ts.append( theChar ); + case _ => // EntityRef + val n = xName ; + xToken(';'); + ts.append( handle.entityRef( tmppos, n ) ); + } + case _ => // text content + appendText(tmppos, ts, xText); + // here xEmbeddedBlock might be true + } + } + } + ts + } /* end content */ + + /** '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag + * | xmlTag1 '/' '>' + */ + /*[Duplicate]*/ def element: Tree = { + val pos1 = pos; + val Tuple2(qname, attrMap) = xTag; + //Console.println("MarkupParser::element("+qname+","+attrMap+")"); + if (ch == '/') { // empty element + xToken('/'); + xToken('>'); + handle.element( pos1, qname, attrMap, new mutable.ListBuffer[Tree] ); + } + else { // handle content + xToken('>'); + debugLastStartElement.push(Pair(pos1,qname)); + val ts = content; + xEndTag( qname ); + debugLastStartElement.pop; + handle.element( pos1, qname, attrMap, ts ); + } + } + + + /** Name ::= (Letter | '_' | ':') (NameChar)* + * + * see [5] of XML 1.0 specification + */ + /*[Duplicate]*/ def xName: String = { + if( xml.Parsing.isNameStart( ch ) ) { + do { + putChar( ch ); + nextch; + } while( xml.Parsing.isNameChar( ch ) ); + val n = cbuf.toString().intern(); + cbuf.setLength( 0 ); + n + } else { + reportSyntaxError( "name expected, but char '"+ch+"' cannot start a name" ); + new String(); + } + } + + + /** scan [S] '=' [S]*/ + /*[Duplicate]*/ def xEQ = { xSpaceOpt; xToken('='); xSpaceOpt } + + /** skip optional space S? */ + /*[Duplicate]*/ def xSpaceOpt = { while( xml.Parsing.isSpace( ch ) ) { nextch; }} + + /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ + /*[Duplicate]*/ def xSpace = { + if (xml.Parsing.isSpace(ch)) { + nextch; xSpaceOpt + } + else { + reportSyntaxError("whitespace expected"); + } + } + +/** '<?' ProcInstr ::= Name [S ({Char} - ({Char}'>?' {Char})]'?>' + * + * see [15] + */ + /*[Duplicate]*/ def xProcInstr: Tree = { + val sb:StringBuffer = new StringBuffer(); + val n = xName; + if( xml.Parsing.isSpace( ch ) ) { + xSpace; + while( true ) { + if( ch=='?' && { sb.append( ch ); nextch; ch == '>' } ) { + sb.setLength( sb.length() - 1 ); + nextch; + return handle.procInstr(tmppos, n.toString(), sb.toString()); + } else + sb.append( ch ); + nextch; + } + }; + xToken('?'); + xToken('>'); + return handle.procInstr(tmppos, n.toString(), sb.toString()); + } + + /** parse character data. + * precondition: xEmbeddedBlock == false (we are not in a scala block) + */ + /*[Duplicate]*/ def xText: String = { + if( xEmbeddedBlock ) Predef.error("internal error: encountered embedded block"); // assert + + if( xCheckEmbeddedBlock ) + return "" + else { + var exit = false; + while( !exit ) { + putChar( ch ); + exit = { nextch; xCheckEmbeddedBlock }||( ch == '<' ) || ( ch == '&' ); + } + val str = cbuf.toString(); + cbuf.setLength( 0 ); + str + } + } + //override type Tree = handle.Tree; + //override type Tree = handle.Tree; + + final val PATTERN = true; + final val EXPR = false; + + val enableEmbeddedExpressions: Boolean = true; + + //val cbuf = new StringBuffer(); + + /** append Unicode character to name buffer*/ + //private def putChar(c: char) = cbuf.append( c ); + + /** xLiteral = element { element } + * @return Scala representation of this xml literal + * precondition: s.xStartsXML == true + */ + def xLiteral: Tree = try { + init; + handle.isPattern = false; + val pos = s.currentPos; + var tree = element; + xSpaceOpt; + // parse more XML ? + if (ch == '<') { + val ts = new mutable.ArrayBuffer[Tree](); + ts.append( tree ); + while( ch == '<' ) { + nextch; + //Console.println("DEBUG 1: I am getting char '"+ch+"'"); // DEBUG + ts.append( element ); + xSpaceOpt; + } + tree = handle.makeXMLseq( pos, ts ); + } + //Console.println("out of xLiteral, parsed:"+tree.toString()); + s.next.token = EMPTY; + s.nextToken(); /* s.fetchToken(); */ + tree + } + catch { + case _:ArrayIndexOutOfBoundsException => + s.syntaxError(debugLastStartElement.top._1, + "missing end tag in XML literal for <" + +debugLastStartElement.top._2+">"); + EmptyTree; + } + + /** @see xmlPattern. resynchronizes after succesful parse + * @return this xml pattern + * precondition: s.xStartsXML == true + */ + def xLiteralPattern:Tree = try { + init; + val oldMode = handle.isPattern; + handle.isPattern = true; + val pos = s.currentPos; + var tree = xPattern; xSpaceOpt; + //if (ch == '<') { + var ts: List[Tree] = List(); + ts = tree :: ts; + + s.next.token = EMPTY; s.nextToken(); /* ?????????? */ + while( token == Tokens.XMLSTART ) {// ??????????????????????????? + //while (ch == '<' /* && lookahead != '-'*/) { + nextch; + //Console.println("DEBUG 2: I am getting char '"+ch+"'"); // DEBUG + ts = xPattern :: ts; + //xSpaceOpt; // ???? + s.next.token = EMPTY; s.nextToken(); /* ?????????? */ + //Console.println("DEBUG 3: resync'ed, token = '"+s+"'"); // DEBUG + } + //Console.println("current token == "+s); + tree = handle.makeXMLseqPat( pos, ts.reverse ); + //} + handle.isPattern = oldMode; + //Console.println("out of xLiteralPattern, parsed:"+tree.toString()); + // s.next.token = EMPTY; // ?? + // s.nextToken(); /* s.fetchToken(); */ // ?? + tree + }catch { + case _:ArrayIndexOutOfBoundsException => + s.syntaxError(debugLastStartElement.top._1, + "missing end tag in XML literal for <" + +debugLastStartElement.top._2+">"); + EmptyTree; + } + + def xEmbeddedExpr:Tree = { + sync; + val b = p.expr(true,false); + if(/*s.*/token != RBRACE) + reportSyntaxError(" expected end of Scala block"); + init; + //Console.println("[out of xScalaExpr s.ch = "+s.ch+" ch="+ch+"]"); + return b + } + + /** xScalaPatterns ::= patterns + */ + def xScalaPatterns: List[Tree] = { + sync; + val b = p.patterns(); + if (/*s.*/token != RBRACE) + reportSyntaxError(" expected end of Scala patterns"); + init; + return b + } + + //var ch: Char = _; + + /** this method assign the next character to ch and advances in input */ + def nextch: Unit = { s.in.next; /*s.xNext;*/ ch = s.in.ch ; pos = s.currentPos; } + + //def lookahead = { s.xLookahead } + + def init: Unit = { + ch = s.in.ch; + pos = s.currentPos; + //Console.println("\ninit! ch = "+ch); + } + + def reportSyntaxError(str: String) = { + s.syntaxError("in XML literal: " + str); + nextch; + } + + def sync: Unit = { + xEmbeddedBlock = false; + s.xSync; + } + + /** '<' xPattern ::= Name [S] { xmlPattern | '{' pattern3 '}' } ETag + * | Name [S] '/' '>' + */ + def xPattern:Tree = { + //Console.println("xPattern"); + val pos1 = pos; + val qname = xName; + debugLastStartElement.push(Pair(pos1,qname)); + xSpaceOpt; + if( ch == '/' ) { // empty tag + nextch; + xToken('>'); + return handle.makeXMLpat( pos1, qname, new mutable.ArrayBuffer[Tree]() ); + }; + + // else: tag with content + xToken('>'); + var ts = new mutable.ArrayBuffer[Tree]; + var exit = false; + while (! exit) { + val pos2 = pos; + if( xEmbeddedBlock ) { + ts ++ xScalaPatterns; + } else + ch match { + case '<' => { // tag + nextch; + if( ch != '/' ) { //child + ts.append( xPattern ); + } else { + exit = true + } + } + case '{' => // embedded Scala patterns + while( ch == '{' ) { + s.in.next; + ts ++ xScalaPatterns; + } + // postcond: xEmbeddedBlock = false; + if (xEmbeddedBlock) Predef.error("problem with embedded block"); // assert + case _ => // teMaxt + appendText( pos2, ts, xText ); + // here xEmbeddedBlock might be true; + //if( xEmbeddedBlock ) throw new ApplicationError("after:"+text); // assert + } + } + xEndTag(qname); + debugLastStartElement.pop; + handle.makeXMLpat(pos1, qname, ts); + } + +} /* class MarkupParser */ +} diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala new file mode 100644 index 0000000000..0634ace4d0 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -0,0 +1,1808 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +import scala.tools.nsc.util.Position; +import util.ListBuffer; +import symtab.Flags; +import Tokens._; + +/** Performs the following context-free rewritings: + * (1) Places all pattern variables in Bind nodes. In a pattern, for identifiers `x': + * x => x @ _ + * x:T => x @ (_ : T) + * + * (2) Removes pattern definitions (PatDef's) as follows: + * If pattern is a simple (typed) identifier: + * val x = e ==> val x = e + * val x: T = e ==> val x: T = e + * + * if there are no variables in pattern + * val p = e ==> e.match (case p => ()) + * + * if there is exactly one variable in pattern + * val x_1 = e.match (case p => (x_1)) + * + * if there is more than one variable in pattern + * val p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) + * val x_1 = t$._1 + * ... + * val x_N = t$._N + * + * (3) Removes function types as follows: + * (argtpes) => restpe ==> scala.Function_n[argtpes, restpe] + * + * (4) Wraps naked case definitions in a match as follows: + * { cases } ==> (x => x.match {cases}), except when already argument to match + */ +[_trait_] abstract class Parsers: SyntaxAnalyzer { + + import global._; + import posAssigner.atPos; + + class Parser(unit: CompilationUnit) { + + val in = new Scanner(unit); + + /** the markup parser */ + val xmlp = new MarkupParser(unit, in, Parser.this, true); + + object treeBuilder extends TreeBuilder { + val global: Parsers.this.global.type = Parsers.this.global; + def freshName(prefix: String): Name = unit.fresh.newName(prefix); + } + import treeBuilder._; + + object symbXMLBuilder extends SymbolicXMLBuilder(treeBuilder, Parser.this, true) { // DEBUG choices + val global: Parsers.this.global.type = Parsers.this.global; + def freshName(prefix: String): Name = unit.fresh.newName(prefix); + } + + /** this is the general parse method + */ + def parse(): Tree = { + val t = compilationUnit(); + accept(EOF); + t + } + +/////// ERROR HANDLING ////////////////////////////////////////////////////// + + private def skip(): unit = { + //System.out.println("<skipping> " + in.token2string(in.token));//DEBUG + var nparens = 0; + var nbraces = 0; + while (true) { + in.token match { + case EOF => + return; + case SEMI => + if (nparens == 0 && nbraces == 0) return; + case NEWLINE => + if (nparens == 0 && nbraces == 0) return; + case RPAREN => + nparens = nparens - 1; + case RBRACE => + if (nbraces == 0) return; + nbraces = nbraces - 1; + case LPAREN => + nparens = nparens + 1; + case LBRACE => + nbraces = nbraces + 1; + case _ => + } + in.nextToken(); + } + } + + def syntaxError(msg: String, skipIt: boolean): unit = + syntaxError(in.currentPos, msg, skipIt); + + def syntaxError(pos: int, msg: String, skipIt: boolean): unit = { + if (pos != in.errpos) { + unit.error(pos, msg); + in.errpos = pos; + } + if (skipIt) skip(); + } + + def accept(token: int): int = { + val pos = in.currentPos; + if (in.token != token) + syntaxError( + if (Position.line(unit.source, in.currentPos) > Position.line(unit.source, in.lastPos)) in.lastPos + else in.currentPos, + in.token2string(token) + " expected but " + + in.token2string(in.token) + " found.", true); + if (in.token == token) in.nextToken(); + pos; + } + + /** SEP = NL | `;' + * NL = `\n' // where allowed + */ + def acceptStatSep(): unit = if (in.token == NEWLINE) in.nextToken() else accept(SEMI); + + def errorTypeTree = TypeTree().setType(ErrorType).setPos(in.currentPos); + def errorTermTree = Literal(Constant(null)).setPos(in.currentPos); + def errorPatternTree = Ident(nme.WILDCARD).setPos(in.currentPos); + +/////// TOKEN CLASSES ////////////////////////////////////////////////////// + + def isModifier: boolean = in.token match { + case ABSTRACT | FINAL | SEALED | PRIVATE | PROTECTED | OVERRIDE | IMPLICIT => true + case _ => false + } + + def isLocalModifier: boolean = in.token match { + case ABSTRACT | FINAL | SEALED => true + case _ => false + } + + def isDefIntro: boolean = in.token match { + case VAL | VAR | DEF | TYPE | OBJECT | + CASEOBJECT | CLASS | CASECLASS | TRAIT => true + case _ => false + } + + def isDclIntro: boolean = in.token match { + case VAL | VAR | DEF | TYPE => true + case _ => false + } + + def isExprIntro: boolean = in.token match { + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | + STRINGLIT | SYMBOLLIT | TRUE | FALSE | NULL | IDENTIFIER | + THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE | + DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true + case _ => false + } + +/////// COMMENT AND ATTRIBUTE COLLECTION ////////////////////////////////////// + + /** Join the comment associated with a definition + */ + def joinComment(trees: => List[Tree]): List[Tree] = { + val buf = in.docBuffer; + if (buf != null) { + in.docBuffer = null; + trees map (t => DocDef(buf.toString(), t) setPos t.pos) + } else trees + } + +/////// TREE CONSTRUCTION //////////////////////////////////////////////////// + + def scalaDot(name: Name): Tree = + Select(Ident(nme.scala_), name); + def scalaAnyRefConstr: Tree = + scalaDot(nme.AnyRef.toTypeName); + def scalaScalaObjectConstr: Tree = + scalaDot(nme.ScalaObject.toTypeName); + def caseClassConstr: Tree = + scalaDot(nme.CaseClass.toTypeName); + + /** Convert tree to formal parameter list + */ + def convertToParams(t: Tree): List[ValDef] = t match { + case Function(params, TypeTree()) => + params + case Ident(_) | Typed(Ident(_), _) => + List(convertToParam(t)); + case Literal(c) if c.tag == UnitTag => + Nil + case _ => + syntaxError(t.pos, "malformed formal parameter list", false); + Nil + } + + /** Convert tree to formal parameter + */ + def convertToParam(tree: Tree): ValDef = + atPos(tree.pos) { + tree match { + case Ident(name) => + ValDef(Modifiers(Flags.PARAM), name, TypeTree(), EmptyTree) + case Typed(Ident(name), tpe) => + ValDef(Modifiers(Flags.PARAM), name, tpe, EmptyTree) + case _ => + syntaxError(tree.pos, "not a legal formal parameter", false); + ValDef(Modifiers(Flags.PARAM), nme.ERROR, errorTypeTree, EmptyTree) + } + } + + /** Convert (qual)ident to type identifier + */ + def convertToTypeId(tree: Tree): Tree = tree match { + case Ident(name) => + Ident(name.toTypeName).setPos(tree.pos) + case Select(qual, name) => + Select(qual, name.toTypeName).setPos(tree.pos) + case _ => + System.out.println(tree);//debug + syntaxError(tree.pos, "identifier expected", false); + errorTypeTree + } + + /** make closure from tree */ + def makeClosure(tree: Tree): Tree = { + val pname: Name = unit.fresh.newName("x$"); + def insertParam(tree: Tree): Tree = tree match { + case Ident(name) => + Select(Ident(pname), name) + case Select(qual, name) => + Select(insertParam(qual), name) + case Apply(fn, args) => + Apply(insertParam(fn), args) + case TypeApply(fn, args) => + TypeApply(insertParam(fn), args) + case _ => + syntaxError(tree.pos, "cannot convert to closure", false); + errorTermTree + } + Function( + List(ValDef(Modifiers(Flags.PARAM), pname, TypeTree(), EmptyTree)), + insertParam(tree)) + } + +/////// OPERAND/OPERATOR STACK ///////////////////////////////////////////////// + + case class OpInfo(operand: Tree, operator: Name, pos: int); + var opstack: List[OpInfo] = Nil; + + def precedence(operator: Name): int = + if (operator eq nme.ERROR) -1 + else { + val firstCh = operator(0); + if (((firstCh >= 'A') && (firstCh <= 'Z')) || + ((firstCh >= 'a') && (firstCh <= 'z'))) + 1 + else + firstCh match { + case '|' => 2 + case '^' => 3 + case '&' => 4 + case '<' | '>' => 5 + case '=' | '!' => 6 + case ':' => 7 + case '+' | '-' => 8; + case '*' | '/' | '%' => 9; + case _ => 10; + } + } + + def reduceStack(isExpr: boolean, base: List[OpInfo], top0: Tree, prec: int, leftAssoc: boolean): Tree = { + var top = top0; + if (opstack != base && + precedence(opstack.head.operator) == prec && + treeInfo.isLeftAssoc(opstack.head.operator) != leftAssoc) { + syntaxError( + opstack.head.pos, + "left- and right-associative operators with same precedence may not be mixed", + false); + } + while (opstack != base && + (prec < precedence(opstack.head.operator) || + (leftAssoc && prec == precedence(opstack.head.operator)))) { + top = atPos(opstack.head.pos) { + makeBinop(isExpr, opstack.head.operand, opstack.head.operator, top) + } + opstack = opstack.tail; + } + top + } + +/////// IDENTIFIERS AND LITERALS //////////////////////////////////////////////////////////// + + final val MINUS: Name = "-"; + final val PLUS : Name = "+"; + final val BANG : Name = "!"; + final val TILDE: Name = "~"; + final val STAR : Name = "*"; + final val BAR : Name = "|"; + final val OPT : Name = "?"; + final val LT : Name = "<"; + + def ident(): Name = + if (in.token == IDENTIFIER) { + val name = in.name.encode; + in.nextToken(); + name + } else { + accept(IDENTIFIER); + nme.ERROR + } + + /** StableRef ::= StableId + * | [Ident `.'] this + * SimpleType ::= StableRef [`.' type] + */ + def stableRef(thisOK: boolean, typeOK: boolean): Tree = { + var t: Tree = null; + if (in.token == THIS) { + t = atPos(in.skipToken()) { This(nme.EMPTY.toTypeName) } + if (!thisOK || in.token == DOT) + t = { selectors(t, typeOK, accept(DOT)) } + } else if (in.token == SUPER) { + t = atPos(in.skipToken()) { + Super(nme.EMPTY.toTypeName, mixinQualifierOpt()) + } + t = atPos(accept(DOT)) { Select(t, ident()) } + if (in.token == DOT) + t = { selectors(t, typeOK, in.skipToken()) } + } else { + val i = atPos(in.currentPos) { Ident(ident()) } + t = i; + if (in.token == DOT) { + val pos = in.skipToken(); + if (in.token == THIS) { + in.nextToken(); + t = atPos(i.pos) { This(i.name.toTypeName) } + if (!thisOK || in.token == DOT) + t = { selectors(t, typeOK, accept(DOT)) } + } else if (in.token == SUPER) { + in.nextToken(); + t = atPos(i.pos) { Super(i.name.toTypeName, mixinQualifierOpt()) } + t = atPos(accept(DOT)) { Select(t, ident())} + if (in.token == DOT) + t = { selectors(t, typeOK, in.skipToken()) } + } else { + t = { selectors(t, typeOK, pos) } + } + } + } + t + } + + def selectors(t: Tree, typeOK: boolean, pos : Int): Tree = + if (typeOK && in.token == TYPE) { + in.nextToken(); + atPos(pos) { SingletonTypeTree(t) } + } else { + val t1 = atPos(pos) { Select(t, ident()); } + if (in.token == DOT) { selectors(t1, typeOK, in.skipToken()) } + else t1 + } + + /** MixinQualifier ::= `[' Id `]' + */ + def mixinQualifierOpt(): Name = + if (in.token == LBRACKET) { + in.nextToken(); + val name = ident().toTypeName; + accept(RBRACKET); + name + } else { + nme.EMPTY.toTypeName + } + + /** StableId ::= Id + * | StableRef `.' Id + * | [Id '.'] super [MixinQualifier] ` `.' Id + */ + def stableId(): Tree = + stableRef(false, false); + + /** QualId ::= Id {`.' Id} + */ + def qualId(): Tree = { + val id = atPos(in.currentPos) { Ident(ident()) } + if (in.token == DOT) { selectors(id, false, in.skipToken()) } + else id + } + + /** SimpleExpr ::= literal + * | symbol [ArgumentExprs] + * | null + */ + def literal(isPattern: boolean, isNegated: boolean): Tree = { + def litToTree() = atPos(in.currentPos) { + Literal( + in.token match { + case CHARLIT => + Constant(in.intVal.asInstanceOf[char]) + case INTLIT => + Constant(in.intVal(isNegated).asInstanceOf[int]) + case LONGLIT => + Constant(in.intVal(isNegated)) + case FLOATLIT => + Constant(in.floatVal(isNegated).asInstanceOf[float]) + case DOUBLELIT => + Constant(in.floatVal(isNegated)) + case STRINGLIT | SYMBOLLIT => + Constant(in.name.toString()) + case TRUE => + Constant(true) + case FALSE => + Constant(false) + case NULL => + Constant(null) + case _ => + syntaxError("illegal literal", true); + null + }) + } + + val isSymLit = in.token == SYMBOLLIT; + val t = litToTree(); + val pos = in.skipToken(); + if (isSymLit) { + atPos(pos) { + var symid = scalaDot(nme.Symbol); + if (isPattern) { symid = convertToTypeId(symid) } + Apply(symid, List(t)) + } + } else { + t + } + } + + def newLineOpt(): unit = if (in.token == NEWLINE) in.nextToken(); + +//////// TYPES /////////////////////////////////////////////////////////////// + + /** TypedOpt ::= [`:' Type] + */ + def typedOpt(): Tree = + if (in.token == COLON) { in.nextToken(); typ() } + else TypeTree(); + + /** RequiresTypedOpt ::= [`:' SimpleType | requires SimpleType] + */ + def requiresTypeOpt(): Tree = + if (in.token == COLON | in.token == REQUIRES) { in.nextToken(); simpleType() } + else TypeTree(); + + /** Types ::= Type {`,' Type} + */ + def types(): List[Tree] = { + val ts = new ListBuffer[Tree] + typ(); + while (in.token == COMMA) { + in.nextToken(); + ts += typ(); + } + ts.toList + } + + /** Type ::= Type1 `=>' Type + * | `(' [Types] `)' `=>' Type + * | Type1 + */ + def typ(): Tree = { + val t = + if (in.token == LPAREN) { + in.nextToken(); + if (in.token == RPAREN) { + in.nextToken(); + atPos(accept(ARROW)) { makeFunctionTypeTree(List(), typ()) } + } else { + val t0 = typ(); + if (in.token == COMMA) { + in.nextToken(); + val ts = new ListBuffer[Tree] + t0 ++ types(); + accept(RPAREN); + atPos (accept(ARROW)) { makeFunctionTypeTree(ts.toList, typ()) } + } else { + accept(RPAREN); t0 + } + } + } else { + type1() + } + if (in.token == ARROW) atPos(in.skipToken()) { + makeFunctionTypeTree(List(t), typ()) } + else t + } + + /** Type1 ::= SimpleType {with SimpleType} [Refinement] + */ + def type1(): Tree = { + val pos = in.currentPos; + var ts = new ListBuffer[Tree] + simpleType(); + while (in.token == WITH) { + in.nextToken(); ts += simpleType() + } + atPos(pos) { + if (in.token == LBRACE) CompoundTypeTree(Template(ts.toList, refinement())) + else makeIntersectionTypeTree(ts.toList) + } + } + + /** SimpleType ::= SimpleType TypeArgs + * | SimpleType `#' Id + * | StableId + * | StableRef `.' type + * | `(' Type `)' + */ + def simpleType(): Tree = { + val pos = in.currentPos; + var t: Tree = + if (in.token == LPAREN) { + in.nextToken(); + val t = typ(); + accept(RPAREN); + t + } else { + val r = stableRef(false, true); + val x = r match { + case SingletonTypeTree(_) => r + case _ => convertToTypeId(r); + } + // System.err.println("SIMPLE_TYPE: " + r.pos + " " + r + " => " + x.pos + " " + x); + x; + } + while (true) { + if (in.token == HASH) + t = atPos(in.skipToken()) { + SelectFromTypeTree(t, ident().toTypeName); + } + else if (in.token == LBRACKET) + t = atPos(pos) { AppliedTypeTree(t, typeArgs()) } + else + return t + } + null; //dummy + } + + /** TypeArgs ::= `[' Types `]' + */ + def typeArgs(): List[Tree] = { + accept(LBRACKET); + val ts = types(); + accept(RBRACKET); + ts + } + +//////// EXPRESSIONS //////////////////////////////////////////////////////// + + /** EqualsExpr ::= `=' Expr + */ + def equalsExpr(): Tree = { + accept(EQUALS); + expr() + } + + /** Exprs ::= Expr {`,' Expr} [ `:' `_' `*' ] + */ + def exprs(): List[Tree] = { + val ts = new ListBuffer[Tree] + expr(true, false); + while (in.token == COMMA) { + in.nextToken(); ts += expr(true, false) + } + ts.toList + } + + /** Expr ::= Bindings `=>' Expr + * | Expr1 + * ResultExpr ::= Bindings `=>' Block + * | Expr1 + * Expr1 ::= if (' Expr `)' [NL] Expr [[`;'] else Expr] + * | try `{' block `}' [catch `{' caseClauses `}'] [finally Expr] + * | while `(' Expr `)' [NL] Expr + * | do Expr [SEP] while `(' Expr `)' + * | for (`(' Enumerators `)' | '{' Enumerators '}') [NL] (yield) Expr + * | throw Expr + * | return [Expr] + * | [SimpleExpr `.'] Id `=' Expr + * | SimpleExpr ArgumentExprs `=' Expr + * | `.' SimpleExpr + * | PostfixExpr [`:' Type1] + * | PostfixExpr match `{' caseClauses `}' + * Bindings ::= Id [`:' Type1] + * | `(' [Binding {`,' Binding}] `)' + * Binding ::= Id [`:' Type] + */ + def expr(): Tree = + expr(false, false); + + def expr(isArgument: boolean, isInBlock: boolean): Tree = in.token match { + case IF => + val pos = in.skipToken(); + accept(LPAREN); + val cond = expr(); + accept(RPAREN); + newLineOpt(); + val thenp = expr(); + val elsep = + if (in.token == ELSE) { in.nextToken(); expr() } + else EmptyTree; + atPos(pos) { If(cond, thenp, elsep) } + case TRY => + atPos(in.skipToken()) { + accept(LBRACE); + val body = block(); + accept(RBRACE); + val catches = + if (in.token == CATCH) { + in.nextToken(); + accept(LBRACE); + val cases = caseClauses(); + accept(RBRACE); + cases + } else List(); + val finalizer = + if (in.token == FINALLY) { in.nextToken(); expr() } + else EmptyTree; + Try(body, catches, finalizer) + } + case WHILE => + val lname: Name = unit.fresh.newName("label$"); + val pos = in.skipToken(); + accept(LPAREN); + val cond = expr(); + accept(RPAREN); + newLineOpt(); + val body = expr(); + atPos(pos) { makeWhile(lname, cond, body) } + case DO => + val lname: Name = unit.fresh.newName("label$"); + val pos = in.skipToken(); + val body = expr(); + if (in.token == SEMI || in.token == NEWLINE) in.nextToken(); + accept(WHILE); + accept(LPAREN); + val cond = expr(); + accept(RPAREN); + atPos(pos) { makeDoWhile(lname, body, cond) } + case FOR => + atPos(in.skipToken()) { + val startToken = in.token; + accept(if (startToken == LBRACE) LBRACE else LPAREN); + val enums = enumerators(); + accept(if (startToken == LBRACE) RBRACE else RPAREN); + newLineOpt(); + if (in.token == YIELD) { + in.nextToken(); makeForYield(enums, expr()) + } else makeFor(enums, expr()) + } + case RETURN => + atPos(in.skipToken()) { + Return(if (isExprIntro) expr() else Literal(())) + } + case THROW => + atPos(in.skipToken()) { + Throw(expr()) + } + case DOT => + atPos(in.skipToken()) { + if (in.token == IDENTIFIER) makeClosure(simpleExpr()) + else { syntaxError("identifier expected", true); errorTermTree } + } + case _ => + var t = postfixExpr(); + if (in.token == EQUALS) { + t match { + case Ident(_) | Select(_, _) | Apply(_, _) => + t = atPos(in.skipToken()) { makeAssign(t, expr()) } + case _ => + } + } else if (in.token == COLON) { + val pos = in.skipToken(); + if (isArgument && in.token == USCORE) { + val pos1 = in.skipToken(); + if (in.token == IDENTIFIER && in.name == nme.STAR) { + in.nextToken(); + t = atPos(pos) { + Typed(t, atPos(pos1) { Ident(nme.WILDCARD_STAR.toTypeName) }) + } + } else { + syntaxError(in.currentPos, "`*' expected", true); + } + } else { + t = atPos(pos) { Typed(t, type1()) } + } + } else if (in.token == MATCH) { + t = atPos(in.skipToken()) { + accept(LBRACE); + val cases = caseClauses(); + accept(RBRACE); + Match(t, cases): Tree + } + } + if (in.token == ARROW) { + t = atPos(in.skipToken()) { + Function(convertToParams(t), if (isInBlock) block() else expr()) + } + } + t + } + + /** PostfixExpr ::= [`.'] InfixExpr [Id] + * InfixExpr ::= PrefixExpr + * | InfixExpr Id InfixExpr + */ + def postfixExpr(): Tree = { + val base = opstack; + var top = prefixExpr(); + while (in.token == IDENTIFIER) { + top = reduceStack( + true, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)); + opstack = OpInfo(top, in.name, in.currentPos) :: opstack; + ident(); + if (isExprIntro) { + top = prefixExpr(); + } else { + val topinfo = opstack.head; + opstack = opstack.tail; + return Select( + reduceStack(true, base, topinfo.operand, 0, true), + topinfo.operator.encode).setPos(topinfo.pos); + } + } + reduceStack(true, base, top, 0, true) + } + + /** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr + */ + def prefixExpr(): Tree = + if (in.token == IDENTIFIER && in.name == MINUS) { + val name = ident(); + in.token match { + case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => literal(false, true) + case _ => atPos(in.currentPos) { Select(simpleExpr(), name) } + } + } else if (in.token == IDENTIFIER && (in.name == PLUS || in.name == TILDE || in.name == BANG)) { + val pos = in.currentPos; + val name = ident(); + atPos(pos) { Select(simpleExpr(), name) } + } else { + simpleExpr() + } + + /* SimpleExpr ::= new SimpleType {`(' [Exprs] `)'} {`with' SimpleType} [TemplateBody] + * | SimpleExpr1 + * SimpleExpr1 ::= literal + * | xLiteral + * | StableRef + * | `(' [Expr] `)' + * | BlockExpr + * | SimpleExpr `.' Id + * | SimpleExpr TypeArgs + * | SimpleExpr1 ArgumentExprs + */ + def simpleExpr(): Tree = { + var t: Tree = null; + var isNew = false; + in.token match { + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | + SYMBOLLIT | TRUE | FALSE | NULL => + t = literal(false, false); + case XMLSTART => + t = xmlp.xLiteral; + //Console.println("successfully parsed XML at "+t); // DEBUG + case IDENTIFIER | THIS | SUPER => + t = stableRef(true, false); + case LPAREN => + val pos = in.skipToken(); + if (in.token == RPAREN) { + in.nextToken(); + t = Literal(()).setPos(pos); + } else { + t = expr(); + if (in.token == COMMA) { + val commapos = in.skipToken(); + val ts = new ListBuffer[Tree] + t ++ exprs(); + accept(RPAREN); + if (in.token == ARROW) { + t = atPos(pos) { + Function(ts.toList map convertToParam, TypeTree()) + } + } else { + syntaxError(commapos, "`)' expected", false); + } + } else { + accept(RPAREN); + } + } + case LBRACE => + t = blockExpr() + case NEW => + t = atPos(in.skipToken()) { + val parents = new ListBuffer[Tree] + simpleType(); + val argss = new ListBuffer[List[Tree]]; + if (in.token == LPAREN) + do { argss += argumentExprs() } while (in.token == LPAREN) + else argss += List(); + while (in.token == WITH) { + in.nextToken(); + parents += simpleType() + } + val stats = if (in.token == LBRACE) templateBody() else List(); + makeNew(parents.toList, stats, argss.toList) + } + isNew = true + case _ => + syntaxError("illegal start of simple expression", true); + t = errorTermTree + } + while (true) { + in.token match { + case DOT => + t = atPos(in.skipToken()) { Select(t, ident()) } + case LBRACKET => + t match { + case Ident(_) | Select(_, _) => + t = atPos(in.currentPos) { TypeApply(t, typeArgs()) } + case _ => + return t; + } + case LPAREN | LBRACE if (!isNew) => + t = atPos(in.currentPos) { Apply(t, argumentExprs()) } + case _ => + return t + } + isNew = false + } + null;//dummy + } + + /** ArgumentExprs ::= `(' [Exprs] `)' + * | BlockExpr + */ + def argumentExprs(): List[Tree] = { + if (in.token == LBRACE) { + List(blockExpr()) + } else { + accept(LPAREN); + val ts = if (in.token == RPAREN) List() else exprs(); + accept(RPAREN); + ts + } + } + + /** BlockExpr ::= `{' CaseClauses | Block `}' + */ + def blockExpr(): Tree = { + val res = atPos(accept(LBRACE)) { + if (in.token == CASE) makeVisitor(caseClauses()) + else block() + } + accept(RBRACE); + res + } + + /** Block ::= BlockStatSeq + */ + def block(): Tree = makeBlock(blockStatSeq(new ListBuffer[Tree])); + + /** CaseClauses ::= CaseClause {CaseClause} + */ + def caseClauses(): List[CaseDef] = { + val ts = new ListBuffer[CaseDef]; + do { ts += caseClause(); + } while (in.token == CASE); + ts.toList + } + + /** caseClause : =>= case Pattern [if PostfixExpr] `=>' Block + */ + def caseClause(): CaseDef = + atPos(accept(CASE)) { + val pat = pattern(); + val guard = + if (in.token == IF) { in.nextToken(); postfixExpr() } + else EmptyTree; + makeCaseDef(pat, guard, atPos(accept(ARROW))(block())) + } + + /** Enumerators ::= Generator {SEP Enumerator} + * Enumerator ::= Generator + * | Expr + */ + def enumerators(): List[Tree] = { + val enums = new ListBuffer[Tree] + generator(); + while (in.token == SEMI || in.token == NEWLINE) { + in.nextToken(); + enums += (if (in.token == VAL) generator() else expr()) + } + enums.toList + } + + /** Generator ::= val Pattern1 `<-' Expr + */ + def generator(): Tree = + atPos(accept(VAL)) { + makeGenerator(pattern1(false), { accept(LARROW); expr() }) + } + +//////// PATTERNS //////////////////////////////////////////////////////////// + + /** Patterns ::= SeqPattern { , SeqPattern } */ + def patterns(): List[Tree] = { + val ts = new ListBuffer[Tree]; + ts += pattern(true); + while (in.token == COMMA) { + in.nextToken(); ts += pattern(true); + } + ts.toList + } + + /** Pattern ::= Pattern1 { `|' Pattern1 } + * SeqPattern ::= SeqPattern1 { `|' SeqPattern1 } + */ + def pattern(seqOK: boolean): Tree = { + val pos = in.currentPos; + val t = pattern1(seqOK); + if (in.token == IDENTIFIER && in.name == BAR) { + val ts = new ListBuffer[Tree] + t; + while (in.token == IDENTIFIER && in.name == BAR) { + in.nextToken(); ts += pattern1(seqOK); + } + atPos(pos) { makeAlternative(ts.toList) } + } else t + } + + def pattern(): Tree = pattern(false); + + /** Pattern1 ::= varid `:' Type1 + * | `_' `:' Type1 + * | Pattern2 + * SeqPattern1 ::= varid `:' Type1 + * | `_' `:' Type1 + * | [SeqPattern2] + */ + def pattern1(seqOK: boolean): Tree = + if (seqOK && !isExprIntro) { + atPos(in.currentPos) { Sequence(List()) } + } else { + val p = pattern2(seqOK); + p match { + case Ident(name) if (treeInfo.isVariableName(name) && in.token == COLON) => + atPos(in.skipToken()) { Typed(p, type1()) } + case _ => + p + } + } + + /* Pattern2 ::= varid [ @ Pattern3 ] + * | Pattern3 + * SeqPattern2 ::= varid [ @ SeqPattern3 ] + * | SeqPattern3 + */ + def pattern2(seqOK: boolean): Tree = { + val p = pattern3(seqOK); + if (in.token == AT) { + p match { + case Ident(name) => + if (name == nme.WILDCARD) { + in.nextToken(); pattern3(seqOK) + } else if (treeInfo.isVariableName(name)) { + atPos(in.skipToken()) { Bind(name, pattern3(seqOK)) } + } else { + p + } + case _ => + p + } + } else p + } + + /* Pattern3 ::= SimplePattern + * | SimplePattern {Id SimplePattern} + * SeqPattern3 ::= SeqSimplePattern [ '*' | '?' | '+' ] + * | SeqSimplePattern {Id SeqSimplePattern} + */ + def pattern3(seqOK: boolean): Tree = { + val base = opstack; + var top = simplePattern(seqOK); + if (seqOK && in.token == IDENTIFIER) { + if (in.name == STAR) + return atPos(in.skipToken())(Star(top)) + else if (in.name == PLUS) + return atPos(in.skipToken())(makePlus(top)) + else if (in.name == OPT) + return atPos(in.skipToken())(makeOpt(top)) + } + while (in.token == IDENTIFIER && in.name != BAR) { + top = reduceStack( + false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)); + opstack = OpInfo(top, in.name, in.currentPos) :: opstack; + ident(); + top = simplePattern(seqOK) + } + reduceStack(false, base, top, 0, true) + } + + /** SimplePattern ::= varid + * | `_' + * | literal + * | `<' xLiteralPattern + * | StableId [ `(' Patterns `)' ] + * | `(' [Pattern] `)' + * SimpleSeqPattern ::= varid + * | `_' + * | literal + * | `<' xLiteralPattern + * | StableId [ `(' Patterns `)' ] + * | `(' Patterns `)' + */ + def simplePattern(seqOK: boolean): Tree = in.token match { + case IDENTIFIER | THIS => + var t = stableId(); + in.token match { + case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => + t match { + case Ident(name) if name == nme.MINUS => + return literal(true, true); + case _ => + } + case _ => + } + if (in.token == LPAREN) { + atPos(in.skipToken()) { + val ps = if (in.token == RPAREN) List() else patterns(); + accept(RPAREN); + Apply(convertToTypeId(t), ps) + } + } else t + case USCORE => + atPos(in.skipToken()) { Ident(nme.WILDCARD) } + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | TRUE | FALSE | NULL => + literal(true, false) + case LPAREN => + val pos = in.skipToken(); + val p = + if (seqOK) atPos(pos) { makeSequence(patterns()) } + else if (in.token != RPAREN) pattern(false); + else Literal(()).setPos(pos); + accept(RPAREN); + p + case XMLSTART => + val r = xmlp.xLiteralPattern; + //Console.println("successfully parsed xml pattern "+r); DEBUG + r + case _ => + syntaxError("illegal start of simple pattern", true); + errorPatternTree + } + +////////// MODIFIERS //////////////////////////////////////////////////////////// + + /** Modifiers ::= {Modifier} + * Modifier ::= final + * | private [ "[" Id "]" ] + * | protected + * | override + * | abstract + */ + def modifiers(): Modifiers = { + def loop(mods: int): int = in.token match { + case ABSTRACT => + loop(addMod(mods, Flags.ABSTRACT)) + case FINAL => + loop(addMod(mods, Flags.FINAL)) + case SEALED => + loop(addMod(mods, Flags.SEALED)) + case PRIVATE => + loop(addMod(mods, Flags.PRIVATE)) + case PROTECTED => + loop(addMod(mods, Flags.PROTECTED)) + case OVERRIDE => + loop(addMod(mods, Flags.OVERRIDE)) + case IMPLICIT => + loop(addMod(mods, Flags.IMPLICIT)) + case _ => + mods + } + var mods = loop(0); + if ((mods & (Flags.ABSTRACT | Flags.OVERRIDE)) == (Flags.ABSTRACT | Flags.OVERRIDE)) + mods = mods & ~(Flags.ABSTRACT | Flags.OVERRIDE) | Flags.ABSOVERRIDE; + Modifiers(mods) + } + + /** LocalClassModifiers ::= {LocalClassModifier} + * LocalClassModifier ::= final + * | private + */ + def localClassModifiers(): Modifiers = { + def loop(mods: int): int = in.token match { + case ABSTRACT => + loop(addMod(mods, Flags.ABSTRACT)) + case FINAL => + loop(addMod(mods, Flags.FINAL)) + case SEALED => + loop(addMod(mods, Flags.SEALED)) + case _ => + mods + } + Modifiers(loop(0)) + } + + private def addMod(mods: int, mod: int): int = { + if ((mods & mod) != 0) + syntaxError(in.currentPos, "repeated modifier", false); + in.nextToken(); + mods | mod; + } + +//////// PARAMETERS ////////////////////////////////////////////////////////// + + /** ParamClauses ::= {`(' [Param {`,' Param}] ')'} + * [`(' implicit Param {`,' Param} `)'] + * Param ::= Id `:' ParamType + * ClassParamClauses ::= {`(' [ClassParam {`' ClassParam}] ')'} + * [`(' implicit ClassParam {`,' ClassParam} `)'] + * ClassParam ::= [[modifiers] (val | var)] Param + */ + def paramClauses(owner: Name, implicitViews: List[Tree], ofCaseClass: boolean): List[List[ValDef]] = { + var implicitmod = 0; + var caseParam = ofCaseClass; + def param(): ValDef = { + atPos(in.currentPos) { + var mods = Modifiers(Flags.PARAM); + if (owner.isTypeName) { + mods = modifiers() | Flags.PARAMACCESSOR; + if (in.token == VAL) { + in.nextToken() + } else if (in.token == VAR) { + mods = mods | Flags.MUTABLE; + in.nextToken() + } else { + if (mods.flags != Flags.PARAMACCESSOR) accept(VAL); + if (!(caseParam)) mods = mods | Flags.PRIVATE | Flags.LOCAL; + } + if (caseParam) mods = mods | Flags.CASEACCESSOR; + } + val name = ident(); + accept(COLON); + val bynamemod = if (in.token == ARROW) Flags.BYNAMEPARAM else 0; + ValDef(mods | implicitmod | bynamemod, name, paramType(), EmptyTree) + } + } + def paramClause(): List[ValDef] = { + val params = new ListBuffer[ValDef]; + if (in.token != RPAREN) { + if (in.token == IMPLICIT) { + if (!implicitViews.isEmpty) + syntaxError("cannot have both view bounds `<%' and implicit parameters", false); + in.nextToken(); + implicitmod = Flags.IMPLICIT + } + params += param(); + while (in.token == COMMA) { + in.nextToken(); params += param() + } + } + params.toList + } + val vds = new ListBuffer[List[ValDef]]; + val pos = in.currentPos; + while (implicitmod == 0 && in.token == LPAREN) { + in.nextToken(); + vds += paramClause(); + accept(RPAREN); + caseParam = false + } + val result = vds.toList; + if (owner == nme.CONSTRUCTOR && + (result.isEmpty || + (!result.head.isEmpty && result.head.head.mods.hasFlag(Flags.IMPLICIT)))) + if (in.token == LBRACKET) + syntaxError(pos, "no type parameters allowed here", false); + else + syntaxError(pos, "auxiliary constructor needs non-implicit parameter list", false); + addImplicitViews(owner, result, implicitViews) + } + + /** ParamType ::= Type | `=>' Type | Type `*' + */ + def paramType(): Tree = + if (in.token == ARROW) + atPos(in.skipToken()) { + AppliedTypeTree( + scalaDot(nme.BYNAME_PARAM_CLASS_NAME.toTypeName), List(typ())) + } + else { + val t = typ(); + if (in.token == IDENTIFIER && in.name == STAR) { + in.nextToken(); + atPos(t.pos) { + AppliedTypeTree( + scalaDot(nme.REPEATED_PARAM_CLASS_NAME.toTypeName), List(t)) + } + } else t + } + + /** TypeParamClauseOpt ::= [`[' TypeParam {`,' TypeParam} `]'] + * TypeParam ::= [`+' | `-'] FunTypeParam + * FunTypeParamClauseOpt ::= [`[' FunTypeParam {`,' FunTypeParam} `]'] + * FunTypeParam ::= Id TypeBounds + */ + def typeParamClauseOpt(owner: Name, implicitViews: ListBuffer[Tree]): List[AbsTypeDef] = { + def typeParam(): AbsTypeDef = { + var mods = Modifiers(Flags.PARAM); + if (owner.isTypeName && in.token == IDENTIFIER) { + if (in.name == PLUS) { + in.nextToken(); + mods = mods | Flags.COVARIANT; + } else if (in.name == MINUS) { + in.nextToken(); + mods = mods | Flags.CONTRAVARIANT; + } + } + val pname = ident(); + val param = atPos(in.currentPos) { typeBounds(mods, pname) } + if (in.token == VIEWBOUND && (implicitViews != null)) + implicitViews += atPos(in.skipToken()) { + makeFunctionTypeTree(List(Ident(pname.toTypeName)), typ()) + } + param + } + val params = new ListBuffer[AbsTypeDef]; + if (in.token == LBRACKET) { + in.nextToken(); + params += typeParam(); + while (in.token == COMMA) { + in.nextToken(); + params += typeParam(); + } + accept(RBRACKET); + } + params.toList + } + + /** TypeBounds ::= [`>:' Type] [`<:' Type] + */ + def typeBounds(mods: Modifiers, name: Name): AbsTypeDef = { + def bound(tok: int, default: Name): Tree = + if (in.token == tok) { in.nextToken(); typ() } + else scalaDot(default.toTypeName); + AbsTypeDef(mods, name.toTypeName, + bound(SUPERTYPE, nme.All), + bound(SUBTYPE, nme.Any)) + } + +//////// DEFS //////////////////////////////////////////////////////////////// + + + /** Import ::= import ImportExpr {`,' ImportExpr} + */ + def importClause(): List[Tree] = { + accept(IMPORT); + val ts = new ListBuffer[Tree] + importExpr(); + while (in.token == COMMA) { + in.nextToken(); ts += importExpr(); + } + ts.toList + } + + /** ImportRef ::= StableId `.' (Id | `_' | ImportSelectors) + */ + def importExpr(): Tree = + atPos(in.currentPos) { + var t: Tree = null; + var pos = 0; + if (in.token == THIS) { + t = atPos(in.currentPos) { This(nme.EMPTY.toTypeName) } + t = atPos(accept(DOT)) { Select(t, ident()) } + pos = accept(DOT); + } else { + val i = atPos(in.currentPos) { Ident(ident()) } + pos = accept(DOT); + if (in.token == THIS) { + in.nextToken(); + t = atPos(i.pos) { This(i.name.toTypeName) } + t = atPos(accept(DOT)) { Select(t, ident()) } + pos = accept(DOT); + } else { + t = i; + } + } + def loop: Tree = + if (in.token == USCORE) { + in.nextToken(); + Import(t, List(Pair(nme.WILDCARD, null))) + } else if (in.token == LBRACE) { + Import(t, importSelectors()) + } else { + val name = ident(); + if (in.token == DOT) { + t = atPos(pos) { Select(t, name) } + pos = accept(DOT); + loop + } else { + Import(t, List(Pair(name, name))); + } + } + loop + } + + /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}' + */ + def importSelectors(): List[Pair[Name, Name]] = { + val names = new ListBuffer[Pair[Name, Name]]; + accept(LBRACE); + var isLast = importSelector(names); + while (!isLast && in.token == COMMA) { + in.nextToken(); + isLast = importSelector(names); + } + accept(RBRACE); + names.toList + } + + /** ImportSelector ::= Id [`=>' Id | `=>' `_'] + */ + def importSelector(names: ListBuffer[Pair[Name, Name]]): boolean = + if (in.token == USCORE) { + in.nextToken(); names += Pair(nme.WILDCARD, null); true + } else { + val name = ident(); + names += Pair( + name, + if (in.token == ARROW) { + in.nextToken(); + if (in.token == USCORE) { in.nextToken(); nme.WILDCARD } else ident() + } else { + name + }); + false + } + + /** Def ::= val PatDef {`,' PatDef} + * | var VarDef {`,' VatDef} + * | def FunDef {`,' FunDef} + * | type TypeDef {`,' TypeDef} + * | TmplDef + * Dcl ::= val ValDcl {`,' ValDcl} + * | var ValDcl {`,' ValDcl} + * | def FunDcl {`,' FunDcl} + * | type TypeDcl {`,' TypeDcl} + */ + def defOrDcl(mods: Modifiers): List[Tree] = { + in.token match { + case VAL => + patDefOrDcl(mods); + case VAR => + varDefOrDcl(mods); + case DEF => + List(funDefOrDcl(mods)); + case TYPE => + in.nextToken(); + List(typeDefOrDcl(mods)) + case _ => + List(tmplDef(mods)) + } + } + + /** PatDef ::= Pattern2 {`,' Pattern2} [`:' Type] `=' Expr + * ValDcl ::= Id {`,' Id} `:' Type + */ + def patDefOrDcl(mods: Modifiers): List[Tree] = { + var newmods = mods; + var lhs = new ListBuffer[Tree]; + do { + in.nextToken(); + lhs += pattern2(false) + } while (in.token == COMMA); + val tp = typedOpt(); + val rhs = + if (tp.isEmpty || in.token == EQUALS) equalsExpr() + else { + newmods = newmods | Flags.DEFERRED; + EmptyTree + } + def mkDefs(p: Tree): List[Tree] = { + //Console.println("DEBUG: p = "+p.toString()); // DEBUG + val trees = + makePatDef(newmods, + if (tp.isEmpty) + p + else + Typed(p, tp), + rhs.duplicate) map atPos(p.pos); + if (rhs == EmptyTree) { + trees match { + case List(ValDef(_, _, _, EmptyTree)) => + case _ => syntaxError(p.pos, "pattern definition may not be abstract", false); + } + } + trees + } + for (val p <- lhs.toList; val d <- mkDefs(p)) yield d + } + + /** VarDef ::= Id {`,' Id} [`:' Type] `=' Expr + * | Id {`,' Id} `:' Type `=' `_' + * VarDcl ::= Id {`,' Id} `:' Type + */ + def varDefOrDcl(mods: Modifiers): List[Tree] = { + var newmods = mods | Flags.MUTABLE; + val lhs = new ListBuffer[Pair[Int, Name]]; + do { + lhs += Pair(in.skipToken(), ident()) + } while (in.token == COMMA); + val tp = typedOpt(); + val rhs = if (tp.isEmpty || in.token == EQUALS) { + accept(EQUALS); + if (tp != EmptyTree && in.token == USCORE) { + in.nextToken(); + EmptyTree + } else + expr(); + } else { + newmods = newmods | Flags.DEFERRED; + EmptyTree + } + for (val Pair(pos, name) <- lhs.toList) yield + atPos(pos) { ValDef(newmods, name, tp.duplicate, rhs.duplicate) } + } + + /** FunDef ::= FunSig `:' Type `=' Expr + * | this ParamClause ParamClauses `=' ConstrExpr + * FunDcl ::= FunSig `:' Type + * FunSig ::= id [FunTypeParamClause] ParamClauses + */ + def funDefOrDcl(mods: Modifiers): Tree = + atPos(in.skipToken()) { + if (in.token == THIS) { + in.nextToken(); + val vparamss = paramClauses(nme.CONSTRUCTOR, List(), false); + accept(EQUALS); + DefDef(mods, nme.CONSTRUCTOR, List(), vparamss, TypeTree(), constrExpr()) + } else { + var newmods = mods; + val name = ident(); + val implicitViews = new ListBuffer[Tree]; + val tparams = typeParamClauseOpt(name, implicitViews); + val vparamss = paramClauses(name, implicitViews.toList, false); + val restype = typedOpt(); + val rhs = + if (restype.isEmpty || in.token == EQUALS) equalsExpr(); + else { + newmods = newmods | Flags.DEFERRED; + EmptyTree + } + DefDef(newmods, name, tparams, vparamss, restype, rhs) + } + } + + /** ConstrExpr ::= SelfInvocation + * | `{' SelfInvocation {SEP BlockStat} `}' + * SelfInvocation ::= this ArgumentExpr + */ + def constrExpr(): Tree = + if (in.token == LBRACE) { + atPos(in.skipToken()) { + val statlist = new ListBuffer[Tree]; + statlist += selfInvocation(); + val stats = + if (in.token == SEMI || in.token == NEWLINE) { in.nextToken(); blockStatSeq(statlist) } + else statlist.toList; + accept(RBRACE); + makeBlock(stats) + } + } else selfInvocation(); + + /** SelfInvocation ::= this ArgumentExprs + */ + def selfInvocation(): Tree = + atPos(accept(THIS)) { Apply(Ident(nme.CONSTRUCTOR), argumentExprs()) } + + /** TypeDef ::= Id `=' Type + * TypeDcl ::= Id TypeBounds + */ + def typeDefOrDcl(mods: Modifiers): Tree = + atPos(in.currentPos) { + val name = ident().toTypeName; + in.token match { + case LBRACKET => + val tparams = typeParamClauseOpt(name, null); + accept(EQUALS); + AliasTypeDef(mods, name, tparams, typ()) + case EQUALS => + in.nextToken(); + AliasTypeDef(mods, name, List(), typ()) + case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | COMMA | RBRACE => + typeBounds(mods | Flags.DEFERRED, name) + case _ => + syntaxError("`=', `>:', or `<:' expected", true); + EmptyTree + } + } + + /** TmplDef ::= ([case] class | trait) ClassDef + * | [case] object ObjectDef + */ + def tmplDef(mods: Modifiers): Tree = in.token match { + case TRAIT => + classDef(mods | Flags.TRAIT | Flags.ABSTRACT); + case CLASS => + classDef(mods); + case CASECLASS => + classDef(mods | Flags.CASE); + case OBJECT => + objectDef(mods); + case CASEOBJECT => + objectDef(mods | Flags.CASE); + case _ => + syntaxError("illegal start of definition", true); + EmptyTree + } + + /** ClassDef ::= ClassSig RequiresTypeOpt ClassTemplate + * ClassSig ::= Id [TypeParamClause] {ClassParamClause} + */ + def classDef(mods: Modifiers): Tree = + atPos(in.skipToken()) { + val name = ident().toTypeName; + val implicitViews = new ListBuffer[Tree]; + val tparams = typeParamClauseOpt(name, implicitViews); + if (mods.hasFlag(Flags.CASE) && in.token != LPAREN) accept(LPAREN); + val vparamss = paramClauses(name, implicitViews.toList, mods.hasFlag(Flags.CASE)); + val thistpe = requiresTypeOpt(); + val template = classTemplate(mods, name, vparamss); + val mods1 = if (mods.hasFlag(Flags.TRAIT) && (template.body forall treeInfo.isInterfaceMember)) + mods | Flags.INTERFACE + else mods; + ClassDef(mods1, name, tparams, thistpe, template) + } + + /** ObjectDef ::= Id ClassTemplate + */ + def objectDef(mods: Modifiers): Tree = + atPos(in.skipToken()) { + val name = ident(); + val template = classTemplate(mods, name, List()); + ModuleDef(mods, name, template) + } + + /** ClassTemplate ::= [`extends' TemplateParents] [[NL] TemplateBody] + * TemplateParents ::= SimpleType {`(' [Exprs] `)'} {`with' SimpleType} + */ + def classTemplate(mods: Modifiers, name: Name, vparamss: List[List[ValDef]]): Template = { + val ret = atPos(in.currentPos) { + val parents = new ListBuffer[Tree]; + val argss = new ListBuffer[List[Tree]]; + if (in.token == EXTENDS) { + in.nextToken(); + val parent = simpleType(); + // System.err.println("classTempl: " + parent); + parents += parent; + if (in.token == LPAREN) + do { argss += argumentExprs() } while (in.token == LPAREN) + else argss += List(); + while (in.token == WITH) { + in.nextToken(); + parents += simpleType() + } + } else argss += List(); + if (name != nme.ScalaObject.toTypeName) + parents += scalaScalaObjectConstr; + if (mods.hasFlag(Flags.CASE)) parents += caseClassConstr; + val ps = parents.toList; + if (in.token == NEWLINE && in.next.token == LBRACE) in.nextToken(); + var body = + if (in.token == LBRACE) { + templateBody() + } else { + if (!(in.token == SEMI || in.token == NEWLINE || in.token == COMMA || in.token == RBRACE)) + syntaxError("`extends' or `{' expected", true); + List() + } + if (!mods.hasFlag(Flags.TRAIT)) Template(ps, vparamss, argss.toList, body) + else Template(ps, body) + } + ret; + } + +////////// TEMPLATES //////////////////////////////////////////////////////////// + + /** TemplateBody ::= `{' [TemplateStat {SEP TemplateStat}] `}' + */ + def templateBody(): List[Tree] = { + accept(LBRACE); + var body = templateStatSeq(); + if (body.isEmpty) body = List(EmptyTree); + accept(RBRACE); + body + } + + /** Refinement ::= `{' [RefineStat {SEP RefineStat}] `}' + */ + def refinement(): List[Tree] = { + accept(LBRACE); + val body = refineStatSeq(); + accept(RBRACE); + body + } + +/////// STATSEQS ////////////////////////////////////////////////////////////// + + /** Packaging ::= package QualId `{' TopStatSeq `}' + */ + def packaging(): Tree = { + atPos(accept(PACKAGE)) { + val pkg = qualId(); + accept(LBRACE); + val stats = topStatSeq(); + accept(RBRACE); + makePackaging(pkg, stats) + } + } + + /** TopStatSeq ::= [TopStat {SEP TopStat}] + * TopStat ::= AttributeClauses Modifiers ClsDef + * | Packaging + * | Import + * | + */ + def topStatSeq(): List[Tree] = { + val stats = new ListBuffer[Tree]; + while (in.token != RBRACE && in.token != EOF) { + if (in.token == PACKAGE) { + stats += packaging() + } else if (in.token == IMPORT) { + stats ++= importClause() + } else if (in.token == CLASS || + in.token == CASECLASS || + in.token == TRAIT || + in.token == OBJECT || + in.token == CASEOBJECT || + in.token == LBRACKET || + isModifier) { + val attrs = attributeClauses(); + (stats ++ + joinAttributes(attrs, joinComment(List(tmplDef(modifiers() | traitAttribute(attrs)))))) + } else if (in.token != SEMI && in.token != NEWLINE) { + syntaxError("illegal start of class or object definition", true); + } + if (in.token != RBRACE && in.token != EOF) acceptStatSep(); + } + stats.toList + } + + /** TemplateStatSeq ::= TemplateStat {SEP TemplateStat} + * TemplateStat ::= Import + * | AttributeClauses Modifiers Def + * | AttributeClauses Modifiers Dcl + * | Expr + * | + */ + def templateStatSeq(): List[Tree] = { + val stats = new ListBuffer[Tree]; + while (in.token != RBRACE && in.token != EOF) { + if (in.token == IMPORT) { + stats ++= importClause() + } else if (isExprIntro) { + stats += expr() + } else if (isDefIntro || isModifier || in.token == LBRACKET) { + val attrs = attributeClauses(); + (stats ++ + joinAttributes(attrs, joinComment(defOrDcl(modifiers() | traitAttribute(attrs))))) + } else if (in.token != SEMI && in.token != NEWLINE) { + syntaxError("illegal start of definition", true); + } + if (in.token != RBRACE) acceptStatSep(); + } + stats.toList + } + + /** AttributeClauses ::= {AttributeClause} + * AttributeClause ::= `[' Attribute {`,' Attribute} `]' [NL] + */ + def attributeClauses(): List[Tree] = { + var attrs = new ListBuffer[Tree]; + while (in.token == LBRACKET) { + in.nextToken(); + attrs += attribute(); + while (in.token == COMMA) { + in.nextToken(); + attrs += attribute() + } + accept(RBRACKET); + newLineOpt(); + } + attrs.toList + } + + /** Attribute ::= StableId [TypeArgs] [`(' [Exprs] `)'] + */ + def attribute(): Tree = { + val pos = in.currentPos; + var t: Tree = convertToTypeId(stableId()); + if (in.token == LBRACKET) + t = atPos(in.currentPos)(AppliedTypeTree(t, typeArgs())); + val args = if (in.token == LPAREN) argumentExprs() else List(); + atPos(pos) { New(t, List(args)) } + } + + def traitAttribute(attrs: List[Tree]) = { + def isTraitAttribute(attr: Tree) = attr match { + case Apply(Select(New(Ident(name)), constr), List()) if (name.toString() == "_trait_") => + true + case _ => + false + } + if (attrs exists isTraitAttribute) Flags.TRAIT else 0 + } + + def joinAttributes(attrs: List[Tree], defs: List[Tree]): List[Tree] = + defs map (defn => + (attrs :\ defn) ((attr, tree) => Attributed(attr, tree) setPos attr.pos)); + + /** RefineStatSeq ::= RefineStat {SEP RefineStat} + * RefineStat ::= Dcl + * | type TypeDef + * | + */ + def refineStatSeq(): List[Tree] = { + val stats = new ListBuffer[Tree]; + while (in.token != RBRACE && in.token != EOF) { + if (isDclIntro) { + stats ++= joinComment(defOrDcl(NoMods)) + } else if (in.token != SEMI && in.token != NEWLINE) { + syntaxError("illegal start of declaration", true); + } + if (in.token != RBRACE) acceptStatSep(); + } + stats.toList + } + + /** BlockStatSeq ::= { BlockStat SEP } [Expr] + * BlockStat ::= Import + * | Def + * | LocalModifiers TmplDef + * | Expr + * | + */ + def blockStatSeq(stats: ListBuffer[Tree]): List[Tree] = { + while ((in.token != RBRACE) && (in.token != EOF) && (in.token != CASE)) { + if (in.token == IMPORT) { + stats ++= importClause(); + acceptStatSep(); + } else if (isExprIntro) { + stats += expr(false, true); + if (in.token != RBRACE && in.token != CASE) acceptStatSep(); + } else if (isDefIntro) { + stats ++= defOrDcl(NoMods); + acceptStatSep(); + if (in.token == RBRACE || in.token == CASE) { + stats += Literal(()).setPos(in.currentPos) + } + } else if (isLocalModifier) { + stats += tmplDef(localClassModifiers()); + acceptStatSep(); + if (in.token == RBRACE || in.token == CASE) { + stats += Literal(()).setPos(in.currentPos) + } + } else if (in.token == SEMI || in.token == NEWLINE) { + in.nextToken(); + } else { + syntaxError("illegal start of statement", true); + } + } + stats.toList + } + + /** CompilationUnit ::= package QualId SEP TopStatSeq + * | package QualId `{' TopStatSeq `}' + * | TopStatSeq + */ + def compilationUnit(): Tree = + atPos(in.currentPos) { + if (in.token == PACKAGE) { + in.nextToken(); + val pkg = qualId(); + if (in.token == SEMI || in.token == NEWLINE) { + in.nextToken(); + makePackaging(pkg, topStatSeq()) + } else { + accept(LBRACE); + val t = makePackaging(pkg, topStatSeq()); + accept(RBRACE); + if (in.token == SEMI || in.token == NEWLINE) in.nextToken(); + t + } + } else { + makePackaging(Ident(nme.EMPTY_PACKAGE_NAME), topStatSeq()) + } + } + } +} + + + +// LocalWords: SOcos diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala new file mode 100644 index 0000000000..5fbf54834e --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -0,0 +1,908 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +import Tokens._; +import scala.tools.nsc.util.{Position, SourceFile} +import SourceFile.{LF, FF, CR, SU} +import scala.tools.nsc.util.CharArrayReader; + +[_trait_] abstract class Scanners: SyntaxAnalyzer { + + import global._; + + /** A class for representing a token's data. */ + class TokenData { + + /** the next token */ + var token: int = EMPTY; + + /** the token's position */ + protected var pos: int = 0; + + /** the first character position after the previous token */ + var lastPos: int = 0; + + def currentPos = pos - 1; + + + /** the name of an identifier or token */ + var name: Name = null; + + /** the base of a number */ + var base: int = 0; + + def copyFrom(td: TokenData) = { + this.token = td.token; + this.pos = td.pos; + this.lastPos = td.lastPos; + this.name = td.name; + this.base = td.base; + } + } + + /** A scanner for the programming language Scala. + * + * @author Matthias Zenger, Martin Odersky, Burak Emir + * @version 1.1 + */ + class Scanner(unit: CompilationUnit) extends TokenData { + + import Tokens._; + import java.lang.{Integer, Long, Float, Double, Character} + + /** Character input reader + */ + val in = new CharArrayReader(unit.source.getContent(), true, syntaxError); + + /** character buffer for literals + */ + val cbuf = new StringBuffer(); + + /** append Unicode character to "lit" buffer + */ + protected def putChar(c: char) = cbuf.append(c); + + /** Clear buffer and set name */ + private def setName = { + name = newTermName(cbuf.toString()); + cbuf.setLength(0) + } + + /** buffer for the documentation comment + */ + var docBuffer: StringBuffer = null; + + /** add the given character to the documentation buffer + */ + protected def putDocChar(c: char): unit = + if (docBuffer != null) docBuffer.append(c); + + /** we need one token lookahead + */ + val next = new TokenData(); + val prev = new TokenData(); + + /** the last error position + */ + var errpos = -1; + + /** a stack which indicates whether line-ends can be statement separators + */ + var sepRegions: List[int] = List(); + +// Get next token ------------------------------------------------------------ + + /** read next token and return last position + */ + def skipToken(): int = { + val p = pos; nextToken(); + // XXX: account for off by one error //??? + p - 1 + } + + def nextToken(): unit = { + if (token == LPAREN) { + sepRegions = RPAREN :: sepRegions + } else if (token == LBRACKET) { + sepRegions = RBRACKET :: sepRegions + } else if (token == LBRACE) { + sepRegions = RBRACE :: sepRegions + } else if (token == CASE) { + sepRegions = ARROW :: sepRegions + } else if (token == RBRACE) { + while (!sepRegions.isEmpty && sepRegions.head != RBRACE) + sepRegions = sepRegions.tail; + if (!sepRegions.isEmpty) + sepRegions = sepRegions.tail + } else if (token == RBRACKET || token == RPAREN || token == ARROW) { + if (!sepRegions.isEmpty && sepRegions.head == token) + sepRegions = sepRegions.tail + } + + val lastToken = token; + if (next.token == EMPTY) { + fetchToken(); + } else { + this.copyFrom(next); + next.token = EMPTY + } + + if (token == CASE) { + prev.copyFrom(this); + fetchToken(); + if (token == CLASS) { + token = CASECLASS; + lastPos = prev.lastPos; + } else if (token == OBJECT) { + token = CASEOBJECT; + lastPos = prev.lastPos; + } else { + next.copyFrom(this); + this.copyFrom(prev); + } + } else if (token == SEMI) { + prev.copyFrom(this); + fetchToken(); + if (token != ELSE) { + next.copyFrom(this); + this.copyFrom(prev); + } + } + + if (afterLineEnd() && inLastOfStat(lastToken) && inFirstOfStat(token) && + (sepRegions.isEmpty || sepRegions.head == RBRACE)) { + next.copyFrom(this); + pos = in.lineStartPos; + token = NEWLINE +/* + } else if (lastToken == RBRACE) { + System.out.println("failing to insert NL after RBRACE: " + sepRegions + " " + + lastPos + " " + in.lineStartPos + " " + pos); +*/ + } +// System.out.println("token: " + toString());//DEBUG + } + + private def afterLineEnd() = ( + lastPos < in.lineStartPos && + (in.lineStartPos <= pos || + lastPos < in.lastLineStartPos && in.lastLineStartPos <= pos) + ); + + /** read next token + */ + private def fetchToken(): unit = { + if (token == EOF) return; + lastPos = in.cpos - 1; // Position.encode(in.cline, in.ccol); + //var index = bp; + while (true) { + in.ch match { + case ' ' | '\t' | CR | LF | FF => + in.next; + case _ => + pos = in.cpos; // Position.encode(in.cline, in.ccol); + in.ch match { + case '\u21D2' => + in.next; token = ARROW; + return; + 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' | // scala-mode: need to understand multi-line case patterns + 'z' => + putChar(in.ch); + in.next; + getIdentRest; // scala-mode: wrong indent for multi-line case blocks + return; + + case '<' => // is XMLSTART? + val last = in.last; + in.next; + last match { + case ' '|'\t'|'\n'|'{'|'('|'>' if xml.Parsing.isNameStart(in.ch) => + token = XMLSTART; + case _ => + // Console.println("found '<', but last is '"+in.last+"'"); // DEBUG + putChar('<'); + getOperatorRest; + } + return; + + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | /*'<' | */ + '>' | '?' | ':' | '=' | '&' | + '|' | '\\' => + putChar(in.ch); + in.next; + getOperatorRest; // XXX + return; + case '/' => + in.next; + if (!skipComment()) { + putChar('/'); + getOperatorRest; + return; + } + + case '0' => + putChar(in.ch); + in.next; + if (in.ch == 'x' || in.ch == 'X') { + in.next; + base = 16 + } else { + base = 8; + } + getNumber; + return; // scala-mode: return is a keyword + case '1' | '2' | '3' | '4' | + '5' | '6' | '7' | '8' | '9' => + base = 10; + getNumber; + return; + case '`' => //" scala-mode: need to understand literals + getStringLit('`'); + token = IDENTIFIER; + return; + case '\"' => //" scala-mode: need to understand literals + getStringLit('\"'); + return; + case '\'' => + in.next; + in.ch 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(in.ch); + in.next; + if (in.ch != '\'') { + getIdentRest; + token = SYMBOLLIT; + return; + } + case _ => + if (Character.isUnicodeIdentifierStart(in.ch)) { + putChar(in.ch); + in.next; + if (in.ch != '\'') { + getIdentRest; + token = SYMBOLLIT; + return; + } + } else { + getlitch() + } + } + if (in.ch == '\'') { + in.next; + token = CHARLIT; + setName + } else { + syntaxError("unclosed character literal"); + } + return; + case '.' => + in.next; + if ('0' <= in.ch && in.ch <= '9') { + putChar('.'); getFraction; + } else { + token = DOT + } + return; + case ';' => + in.next; token = SEMI; + return; + case ',' => + in.next; token = COMMA; + return; + case '(' => //scala-mode: need to understand character quotes + in.next; token = LPAREN; + return; + case '{' => + in.next; token = LBRACE; + return; + case ')' => + in.next; token = RPAREN; + return; + case '}' => + in.next; token = RBRACE; + return; + case '[' => + in.next; token = LBRACKET; + return; + case ']' => + in.next; token = RBRACKET; + return; + case SU => + if (!in.hasNext) token = EOF; + else { + syntaxError("illegal character"); + in.next + } + return; + case _ => + if (Character.isUnicodeIdentifierStart(in.ch)) { + putChar(in.ch); + in.next; + getIdentRest; + } else if (isSpecial(in.ch)) { + putChar(in.ch); + getOperatorRest; + } else { + syntaxError("illegal character"); + in.next; + } + return; + } + } + } + } + + private def skipComment(): boolean = + if (in.ch == '/') { + do { + in.next; + } while ((in.ch != CR) && (in.ch != LF) && (in.ch != SU)); + true + } else if (in.ch == '*') { + docBuffer = null; + var openComments = 1; + in.next; + if (in.ch == '*') docBuffer = new StringBuffer("/**"); + while (openComments > 0) { + do { + do { + if (in.ch == '/') { + in.next; putDocChar(in.ch); + if (in.ch == '*') { + in.next; putDocChar(in.ch); + openComments = openComments + 1; + } + } + in.next; putDocChar(in.ch); + } while (in.ch != '*' && in.ch != SU); + while (in.ch == '*') { + in.next; putDocChar(in.ch); + } + } while (in.ch != '/' && in.ch != SU); + if (in.ch == '/') in.next; + else syntaxError("unclosed comment"); + openComments = openComments - 1; + } + true + } else { + false + } + + def inFirstOfStat(token: int) = token match { + case EOF | ELSE | CASE | EXTENDS | WITH | YIELD | CATCH | FINALLY | MATCH | + REQUIRES | COMMA | SEMI | NEWLINE | DOT | USCORE | COLON | EQUALS | ARROW | + LARROW | SUBTYPE | VIEWBOUND | SUPERTYPE | HASH | AT | + RPAREN | RBRACKET | RBRACE => + false + case _ => + true + } + + def inLastOfStat(token: int) = token match { + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | + IDENTIFIER | THIS | NULL | TRUE | FALSE | RETURN | USCORE | + RPAREN | RBRACKET | RBRACE => + true + case _ => + false + } + +// Identifiers --------------------------------------------------------------- + + def isIdentStart(c: char): boolean = ( + ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'a') || + (c == '_') || (c == '$') || + Character.isUnicodeIdentifierStart(c) + ); + + def isIdentPart(c: char) = ( + isIdentStart(c) || + ('0' <= c && c <= '9') || + Character.isUnicodeIdentifierPart(c) + ); + + def isSpecial(c: char) = { + val chtp = Character.getType(c); + chtp == Character.MATH_SYMBOL || chtp == Character.OTHER_SYMBOL; + } + + private def getIdentRest: unit = + while (true) { + in.ch 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(in.ch); + in.next; + case '_' => + putChar(in.ch); + in.next; + getIdentOrOperatorRest; + return; + case SU => + setName; + token = name2token(name); + return; + case _ => + if(java.lang.Character.isUnicodeIdentifierPart(in.ch)) { + putChar(in.ch); + in.next + } else { + setName; + token = name2token(name); + return + } + } + } + + private def getOperatorRest: unit = + while (true) { + in.ch match { + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | '<' | + '>' | '?' | ':' | '=' | '&' | + '|' | '\\' => + putChar(in.ch); + in.next + case '/' => + in.next; + if (skipComment()) { + setName; + token = name2token(name); + return; + } else { + putChar('/'); + } + case _ => + if (isSpecial(in.ch)) { + putChar(in.ch); + in.next; + } else { + setName; + token = name2token(name); + return; + } + } + } + + private def getIdentOrOperatorRest: unit = + if (isIdentPart(in.ch)) + getIdentRest + else in.ch match { + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | '<' | + '>' | '?' | ':' | '=' | '&' | + '|' | '\\' | '/' => + getOperatorRest; + case _ => + if (isSpecial(in.ch)) getOperatorRest + else { + setName; + token = name2token(name) + } + } + + private def getStringLit(delimiter: char): unit = { + in.next; + while (in.ch != delimiter && (in.isUnicode || in.ch != CR && in.ch != LF && in.ch != SU)) { + getlitch(); + } + if (in.ch == delimiter) { + token = STRINGLIT; + setName; + in.next + } else { + syntaxError("unclosed string literal"); + } + } + +// Literals ----------------------------------------------------------------- + + /** read next character in character or string literal: + */ + protected def getlitch() = + if (in.ch == '\\') { + in.next; + if ('0' <= in.ch && in.ch <= '7') { + val leadch: char = in.ch; + var oct: int = in.digit2int(in.ch, 8); + in.next; + if ('0' <= in.ch && in.ch <= '7') { + oct = oct * 8 + in.digit2int(in.ch, 8); + in.next; + if (leadch <= '3' && '0' <= in.ch && in.ch <= '7') { + oct = oct * 8 + in.digit2int(in.ch, 8); + in.next; + } + } + putChar(oct.asInstanceOf[char]); + } else { + in.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 _ => + syntaxError(in.cpos - 1, // Position.encode(in.cline, in.ccol - 1), + "invalid escape character"); + putChar(in.ch); + } + in.next + } + } else { + putChar(in.ch); + in.next; + } + + /** read fractional part and exponent of floating point number + * if one is present. + */ + protected def getFraction = { + token = DOUBLELIT; + while ('0' <= in.ch && in.ch <= '9') { + putChar(in.ch); + in.next; + } + if (in.ch == 'e' || in.ch == 'E') { + val lookahead = in.copy; + lookahead.next; + if (lookahead.ch == '+' || lookahead.ch == '-') { + lookahead.next; + } + if ('0' <= lookahead.ch && lookahead.ch <= '9') { + putChar(in.ch); + in.next; + if (in.ch == '+' || in.ch == '-') { + putChar(in.ch); + in.next; + } + while ('0' <= in.ch && in.ch <= '9') { + putChar(in.ch); + in.next; + } + } + token = DOUBLELIT; + } + if ((in.ch == 'd') || (in.ch == 'D')) { + putChar(in.ch); + in.next; + token = DOUBLELIT; + } else if ((in.ch == 'f') || (in.ch == 'F')) { + putChar(in.ch); + in.next; + token = FLOATLIT; + } + setName + } + + /** convert name to long value + */ + def intVal(negated: boolean): long = { + if (token == CHARLIT && !negated) { + if (name.length > 0) name(0) else 0 + } else { + var value: long = 0; + val divider = if (base == 10) 1 else 2; + val limit: long = + if (token == LONGLIT) Long.MAX_VALUE else Integer.MAX_VALUE; + var i = 0; + val len = name.length; + while (i < len) { + val d = in.digit2int(name(i), base); + if (d < 0) { + syntaxError("malformed integer number"); + return 0; + } + if (value < 0 || + limit / (base / divider) < value || + limit - (d / divider) < value * (base / divider) && + !(negated && limit == value * base - 1 + d)) { + syntaxError("integer number too large"); + return 0; + } + value = value * base + d; + i = i + 1; + } + if (negated) -value else value + } + } + + def intVal: long = intVal(false); + + /** convert name, base to double value + */ + def floatVal(negated: boolean): double = { + val limit: double = + if (token == DOUBLELIT) Double.MAX_VALUE else Float.MAX_VALUE; + try { + val value = Double.valueOf(name.toString()).doubleValue(); + if (value > limit) + syntaxError("floating point number too large"); + if (negated) -value else value + } catch { + case _: NumberFormatException => + syntaxError("malformed floating point number"); + 0.0 + } + } + + def floatVal: double = floatVal(false); + + /** read a number into name and set base + */ + protected def getNumber:unit = { + while (in.digit2int(in.ch, if (base < 10) 10 else base) >= 0) { + putChar(in.ch); + in.next; + } + token = INTLIT; + if (base <= 10 && in.ch == '.') { + val lookahead = in.copy; + lookahead.next; + lookahead.ch match { + case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | + '8' | '9' | 'd' | 'D' | 'e' | 'E' | 'f' | 'F' => + putChar(in.ch); + in.next; + return getFraction + case _ => + if (!isIdentStart(lookahead.ch)) { + putChar(in.ch); + in.next; + return getFraction + } + } + } + if (base <= 10 && + (in.ch == 'e' || in.ch == 'E' || + in.ch == 'f' || in.ch == 'F' || + in.ch == 'd' || in.ch == 'D')) { + return getFraction + } + setName; + if (in.ch == 'l' || in.ch == 'L') { + in.next; + token = LONGLIT; + } + } + +// XML lexing---------------------------------------------------------------- + def xSync = { + token = NEWLINE; // avoid getting NEWLINE from nextToken if last was RBRACE + //in.next; + nextToken(); + } + +// Errors ----------------------------------------------------------------- + + /** generate an error at the given position + */ + def syntaxError(pos: int, msg: String): unit = { + unit.error(pos, msg); + token = ERROR; + errpos = pos; + } + + /** generate an error at the current token position + */ + def syntaxError(msg: String): unit = syntaxError(pos, msg); + +// Keywords ----------------------------------------------------------------- + + /** Keyword array; maps from name indices to tokens */ + private var key: Array[byte] = _; + private var maxKey = 0; + private var tokenName = new Array[Name](128); + + { + var tokenCount = 0; + + // Enter keywords + + def enterKeyword(n: Name, tokenId: int): unit = { + while (tokenId >= tokenName.length) { + val newTokName = new Array[Name](tokenName.length * 2); + System.arraycopy(tokenName, 0, newTokName, 0, newTokName.length); + tokenName = newTokName; + } + tokenName(tokenId) = n; + if (n.start > maxKey) maxKey = n.start; + if (tokenId >= tokenCount) tokenCount = tokenId + 1; + } + + enterKeyword(nme.ABSTRACTkw, ABSTRACT); + enterKeyword(nme.CASEkw, CASE); + enterKeyword(nme.CATCHkw, CATCH); + enterKeyword(nme.CLASSkw, CLASS); + enterKeyword(nme.DEFkw, DEF); + enterKeyword(nme.DOkw, DO); + enterKeyword(nme.ELSEkw, ELSE); + enterKeyword(nme.EXTENDSkw, EXTENDS); + enterKeyword(nme.FALSEkw, FALSE); + enterKeyword(nme.FINALkw, FINAL); + enterKeyword(nme.FINALLYkw, FINALLY); + enterKeyword(nme.FORkw, FOR); + enterKeyword(nme.IFkw, IF); + enterKeyword(nme.IMPLICITkw, IMPLICIT); + enterKeyword(nme.IMPORTkw, IMPORT); + enterKeyword(nme.MATCHkw, MATCH); + enterKeyword(nme.REQUIRESkw, REQUIRES); + enterKeyword(nme.NEWkw, NEW); + enterKeyword(nme.NULLkw, NULL); + enterKeyword(nme.OBJECTkw, OBJECT); + enterKeyword(nme.OVERRIDEkw, OVERRIDE); + enterKeyword(nme.PACKAGEkw, PACKAGE); + enterKeyword(nme.PRIVATEkw, PRIVATE); + enterKeyword(nme.PROTECTEDkw, PROTECTED); + enterKeyword(nme.RETURNkw, RETURN); + enterKeyword(nme.SEALEDkw, SEALED); + enterKeyword(nme.SUPERkw, SUPER); + enterKeyword(nme.THISkw, THIS); + enterKeyword(nme.THROWkw, THROW); + enterKeyword(nme.TRAITkw, TRAIT); + enterKeyword(nme.TRUEkw, TRUE); + enterKeyword(nme.TRYkw, TRY); + enterKeyword(nme.TYPEkw, TYPE); + enterKeyword(nme.VALkw, VAL); + enterKeyword(nme.VARkw, VAR); + enterKeyword(nme.WHILEkw, WHILE); + enterKeyword(nme.WITHkw, WITH); + enterKeyword(nme.YIELDkw, YIELD); + enterKeyword(nme.DOTkw, DOT); + enterKeyword(nme.USCOREkw, USCORE); + enterKeyword(nme.COLONkw, COLON); + enterKeyword(nme.EQUALSkw, EQUALS); + enterKeyword(nme.ARROWkw, ARROW); + enterKeyword(nme.LARROWkw, LARROW); + enterKeyword(nme.SUBTYPEkw, SUBTYPE); + enterKeyword(nme.VIEWBOUNDkw, VIEWBOUND); + enterKeyword(nme.SUPERTYPEkw, SUPERTYPE); + enterKeyword(nme.HASHkw, HASH); + enterKeyword(nme.ATkw, AT); + + // Build keyword array + key = new Array[byte](maxKey+1); + for (val i <- Iterator.range(0, maxKey + 1)) + key(i) = IDENTIFIER; + for (val j <- Iterator.range(0, tokenCount)) + if (tokenName(j) != null) + key(tokenName(j).start) = j.asInstanceOf[byte]; + + } + +// Token representation ----------------------------------------------------- + + /** Convert name to token */ + def name2token(name: Name): int = + if (name.start <= maxKey) key(name.start) else IDENTIFIER; + + /** Returns the string representation of given token. */ + def token2string(token: int): String = token match { + case IDENTIFIER => + "identifier"/* + \""+name+"\""*/ + case CHARLIT => + "character literal" + case INTLIT => + "integer literal" + case LONGLIT => + "long literal" + case FLOATLIT => + "float literal" + case DOUBLELIT => + "double literal" + case STRINGLIT => + "string literal" + case SYMBOLLIT => + "symbol literal" + case LPAREN => + "'('" + case RPAREN => + "')'" + case LBRACE => + "'{'" + case RBRACE => + "'}'" + case LBRACKET => + "'['" + case RBRACKET => + "']'" + case EOF => + "eof" + case ERROR => + "something" + case SEMI => + "';'" + case NEWLINE => + "';'" + case COMMA => + "','" + case CASECLASS => + "case class" + case CASEOBJECT => + "case object" + case XMLSTART => + "$XMLSTART$<" + case _ => + try { + "'" + tokenName(token) + "'" + } catch { + case _: ArrayIndexOutOfBoundsException => + "'<" + token + ">'" + case _: NullPointerException => + "'<(" + token + ")>'" + } + } + + 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 NEWLINE => + ";" + case COMMA => + "," + case _ => + token2string(token) + } + + /** INIT: read lookahead character and token. + */ + in.next; + nextToken(); + } +} diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala new file mode 100644 index 0000000000..4fc234bead --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala @@ -0,0 +1,349 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +import scala.tools.nsc.util.Position; +import scala.Iterator; +import scala.collection.immutable.{ Map, ListMap }; +import scala.collection.mutable; +import scala.xml.{Text,TextBuffer}; + +import symtab.Flags.MUTABLE; + +/** this class builds instance of Tree that represent XML */ +abstract class SymbolicXMLBuilder(make: TreeBuilder, p: Parsers # Parser, preserveWS: Boolean ) { + + val global: Global; + import global._; + import posAssigner.atPos; + + //import scala.tools.scalac.ast.{TreeList => myTreeList} + + var isPattern:Boolean = _; + + import nme.{ + _Attribute , + _MetaData , + _NamespaceBinding , + _NodeBuffer , + + _Null , + + _PrefixedAttribute , + _UnprefixedAttribute , + _Elem , + _Seq , + _immutable , + _mutable , + _append , + _plus , + _collection , + _toList , + _xml , + _Comment , + _CharData , + _Node , + _ProcInstr , + _Text , + _EntityRef , + _md, + _scope, + _tmpscope}; + + + // convenience methods + private def LL[A](x:A*):List[List[A]] = List(List(x:_*)); + + private def _scala(name: Name) = Select(Ident(nme.scala_), name); + + private def _scala_Seq = _scala( _Seq ); + private def _scala_xml(name: Name) = Select( _scala(_xml), name ); + + private def _scala_xml_MetaData = _scala_xml( _MetaData ); + private def _scala_xml_NamespaceBinding = _scala_xml( _NamespaceBinding ); + private def _scala_xml_Null = _scala_xml( _Null ); + private def _scala_xml_PrefixedAttribute = _scala_xml(_PrefixedAttribute); + private def _scala_xml_UnprefixedAttribute= _scala_xml(_UnprefixedAttribute); + private def _scala_xml_Node = _scala_xml( _Node ); + private def _scala_xml_NodeBuffer = _scala_xml( _NodeBuffer ); + private def _scala_xml_EntityRef = _scala_xml( _EntityRef ); + private def _scala_xml_Comment = _scala_xml( _Comment ); + private def _scala_xml_CharData = _scala_xml( _CharData ); + private def _scala_xml_ProcInstr = _scala_xml( _ProcInstr ); + private def _scala_xml_Text = _scala_xml( _Text ); + private def _scala_xml_Elem = _scala_xml( _Elem ); + private def _scala_xml_Attribute = _scala_xml( _Attribute ); + + + private def bufferToArray(buf: mutable.Buffer[Tree]): Array[Tree] = { + val arr = new Array[Tree]( buf.length ); + var i = 0; + for (val x <- buf.elements) { arr(i) = x; i = i + 1; } + arr; + } + + // create scala xml tree + + /** + * @arg namespace: a Tree of type defs.STRING_TYPE + * @arg label: a Tree of type defs.STRING_TYPE + * @todo map: a map of attributes !!! + */ + + protected def mkXML(pos: int, isPattern: boolean, pre: Tree, label: Tree, attrs: /*Array[*/Tree/*]*/ , scope:Tree, children: mutable.Buffer[Tree]): Tree = { + if( isPattern ) { + //val ts = new mutable.ArrayBuffer[Tree](); + convertToTextPat( children ); + atPos (pos) { + Apply( _scala_xml_Elem, List( + pre, label, Ident( nme.WILDCARD ) /* attributes? */ , Ident( nme.WILDCARD )) /* scope? */ ::: children.toList ) + } + } else { + var ab = List(pre, label, attrs, scope); + if(( children.length ) > 0 ) + ab = ab ::: List(Typed(makeXMLseq(pos, children), Ident(nme.WILDCARD_STAR.toTypeName))); + atPos(pos) { New( _scala_xml_Elem, List(ab) )} + } + } + + final def entityRef( pos:int, n: String ) = { + atPos(pos) { New( _scala_xml_EntityRef, LL(Literal(Constant( n )))) }; + + }; + // create scala.xml.Text here <: scala.xml.Node + final def text( pos: Int, txt:String ):Tree = { + //makeText( isPattern, gen.mkStringLit( txt )); + val txt1 = Literal(Constant(txt)); + atPos(pos) { + if( isPattern ) + makeTextPat( txt1 ); + else + makeText1( txt1 ); + } + } + + // create scala.xml.Text here <: scala.xml.Node + def makeTextPat(txt:Tree ) = Apply(_scala_xml_Text, List(txt)); + def makeText1(txt:Tree ) = New( _scala_xml_Text, LL( txt )); + + // create + def comment( pos: int, text: String ):Tree = + atPos(pos) { Comment( Literal(Constant(text))) }; + + // create + def charData( pos: int, txt: String ):Tree = + atPos(pos) { CharData( Literal(Constant(txt))) }; + + // create scala.xml.Text here <: scala.xml.Node + def procInstr( pos: int, target: String, txt: String ) = + atPos(pos) { ProcInstr(Literal(Constant(target)), Literal(Constant(txt))) }; + + protected def CharData(txt: Tree) = New( _scala_xml_CharData, LL(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: int, n: String, args: mutable.Buffer[Tree]): Tree = + mkXML(pos, + true, + Ident( nme.WILDCARD ), + Literal(Constant(n)), + null, //Predef.Array[Tree](), + null, + args); + + protected def convertToTextPat(t: Tree): Tree = t match { + case _:Literal => makeTextPat(t); + case _ => t + } + + protected def convertToTextPat(buf: mutable.Buffer[Tree]): Unit = { + var i = 0; while( i < buf.length ) { + val t1 = buf( i ); + val t2 = convertToTextPat( t1 ); + if (!t1.eq(t2)) { + buf.remove(i); + buf.insert(i,t2); + } + i = i + 1; + } + } + + def freshName(prefix:String): Name; + + def isEmptyText(t:Tree) = t match { + case Literal(Constant("")) => true; + case _ => false; + } + def makeXMLseq( pos:int, args:mutable.Buffer[Tree] ) = { + var _buffer = New( _scala_xml_NodeBuffer, List(Nil)); + val it = args.elements; + while( it.hasNext ) { + val t = it.next; + if( !isEmptyText( t )) { + _buffer = Apply(Select(_buffer, _plus), List( t )); + } + } + atPos(pos) { Select(_buffer, _toList) }; + } + + def makeXMLseqPat( pos:int, args:List[Tree] ) = atPos(pos) {Apply( _scala_Seq, args )}; + + + /** returns Some(prefix) if pre:name, None otherwise */ + def getPrefix( name:String ):Option[String] = { + val i = name.indexOf(':'); + if( i != -1 ) Some( name.substring(0, i) ) else None + } + + /** splits */ + protected def qualifiedAttr( pos:Int, namespace:String, name:String ):Pair[String,String] = { + getPrefix( name ) match { + case Some( pref ) => + val newLabel = name.substring( pref.length()+1, name.length() ); + // if( newLabel.indexOf(':') != -1 ) syntaxError + Pair( "namespace$"+pref, newLabel ); + case None => + Pair( namespace, name ); + } + } + protected def qualified( pos:Int, name:String ):Pair[String,String] = + getPrefix( name ) match { + case Some( pref ) => + val newLabel = name.substring( pref.length()+1, name.length() ); + // if( newLabel.indexOf(':') != -1 ) syntaxError + Pair( "namespace$"+pref, newLabel ); + case None => + Pair( "namespace$default", name ); + } + + + /** makes an element */ + def element(pos: int, qname: String, attrMap: mutable.Map[String,Tree], args: mutable.Buffer[Tree]): Tree = { + //Console.println("SymbolicXMLBuilder::element("+pos+","+qname+","+attrMap+","+args+")"); + var setNS = new mutable.HashMap[String, Tree]; + + var tlist: List[Tree] = List(); + + /* pre can be null */ + def handleNamespaceBinding(pre:String , uri:Tree): Unit = { + val t = Assign(Ident(_tmpscope), New( _scala_xml_NamespaceBinding, + LL(Literal(Constant(pre)), uri, Ident( _tmpscope)))); + tlist = t :: tlist; + //Console.println("SymbolicXMLBuilder::handleNamespaceBinding:"); + //Console.println(t.toString()); + } + + /* DEBUG */ + val attrIt = attrMap.keys; + while( attrIt.hasNext ) { + val z = attrIt.next; + if( z.startsWith("xmlns") ) { // handle namespace + val i = z.indexOf(':'); + if( i == -1 ) + handleNamespaceBinding(null, attrMap( z )); + //setNS.update("default", attrMap( z ) ); + else { + val zz = z.substring( i+1, z.length() ); + //setNS.update( zz, attrMap( z ) ); + handleNamespaceBinding(zz, attrMap( z )); + } + attrMap -= z; + } + } + + val moreNamespaces = (0 < tlist.length); + + /* */ + val i = qname.indexOf(':'); + + var newlabel = qname; + val pre = getPrefix(qname) match { + case Some(p) => + newlabel = qname.substring(p.length()+1, qname.length()); + p; + case None => + null + } + var tlist2: List[Tree] = List(); + + // make attributes + + def handlePrefixedAttribute(pre:String, key:String, value:Tree): Unit = { + val t = atPos(pos) { + Assign(Ident(_md), New( _scala_xml_PrefixedAttribute, + LL( + Literal(Constant(pre)), + Literal(Constant(key)), + value, + Ident(_md) + )))}; + tlist2 = t :: tlist2; + // Console.println("SymbolicXMLBuilder::handlePrefixed :"); + // Console.println(t.toString()); + } + + def handleUnprefixedAttribute(key:String, value:Tree): Unit = { + val t = atPos(pos) { + Assign(Ident(_md), New(_scala_xml_UnprefixedAttribute, + LL(Literal(Constant(key)),value,Ident(_md)) + ))}; + tlist2 = t :: tlist2; + } + + var it = attrMap.elements; + while (it.hasNext) { + val ansk = it.next; + getPrefix(ansk._1) match { + case Some(pre) => + val key = ansk._1.substring(pre.length()+1, ansk._1.length()); + handlePrefixedAttribute(pre, key, ansk._2); + case None => + handleUnprefixedAttribute(ansk._1, ansk._2); + } + } + // attrs + + + val moreAttributes = (0 < tlist2.length); + + var ts: List[Tree] = tlist; + var ts2: List[Tree] = List(); + + if(moreAttributes) { + ts2 = atPos(pos) {ValDef(Modifiers(MUTABLE), + _md, + _scala_xml_MetaData, + _scala_xml_Null)} :: tlist2; + } + if(moreNamespaces) { + ts = atPos(pos) { + ValDef(Modifiers(MUTABLE), + _tmpscope, + _scala_xml_NamespaceBinding, + Ident(_scope))} :: ts; + + ts2 = ValDef(NoMods, _scope, _scala_xml_NamespaceBinding, Ident(_tmpscope)) :: ts2; + } + + val makeSymbolicAttrs = { + if (moreAttributes) Ident(_md) else _scala_xml_Null; + } + + var t = mkXML(pos, + false, + Literal(Constant(pre)) /* can be null */ , + Literal(Constant(newlabel)):Tree, + makeSymbolicAttrs, + Ident(_scope), + args); + + atPos(pos) { Block(ts, Block( ts2, t)) } + } +} + diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala new file mode 100644 index 0000000000..b4a3b507b3 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -0,0 +1,25 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +/** An nsc sub-component. + */ +abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParsers with Scanners { + val phaseName = "parser"; + def newPhase(prev: Phase): StdPhase = new ParserPhase(prev); + class ParserPhase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) { + def apply(unit: global.CompilationUnit): unit = { + global.informProgress("parsing " + unit); + unit.body = new Parser(unit).parse(); + } + } + //Moez addition. I wished not to add/modify here, but the fact that Parsers + // are NOT accessible (because of Parsers' self type) except in SyntaxAnalyzer + // had bitten me, and thus I had to add the following code here. + def interpreterParse(unit: global.CompilationUnit): List[global.Tree] = + new Parser(unit).templateStatSeq() +} + diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala new file mode 100644 index 0000000000..b99ee08811 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -0,0 +1,97 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +object Tokens { + + /** special tokens */ + final val EMPTY = -3; + final val UNDEF = -2; + final val ERROR = -1; + final val EOF = 0; + + /** literals */ + final val CHARLIT = 1; + final val INTLIT = 2; + final val LONGLIT = 3; + final val FLOATLIT = 4; + final val DOUBLELIT = 5; + final val STRINGLIT = 6; + final val SYMBOLLIT = 7; + + /** identifier */ + final val IDENTIFIER = 10; + + /** keywords */ + final val IF = 20; + final val FOR = 21; + final val ELSE = 22; + final val THIS = 23; + final val NULL = 24; + final val NEW = 25; + final val WITH = 26; + final val SUPER = 27; + final val CASE = 28; + final val CASECLASS = 29; + final val CASEOBJECT = 30; + final val VAL = 31; + final val ABSTRACT = 32; + final val FINAL = 33; + final val PRIVATE = 34; + final val PROTECTED = 35; + final val OVERRIDE = 36; + final val IMPLICIT = 37; + final val VAR = 38; + final val DEF = 39; + final val TYPE = 40; + final val EXTENDS = 41; + final val TRUE = 42; + final val FALSE = 43; + final val OBJECT = 44; + final val CLASS = 45; + + final val IMPORT = 46; + final val PACKAGE = 47; + final val YIELD = 48; + final val DO = 49; + final val TRAIT = 50; + final val SEALED = 51; + final val THROW = 52; + final val TRY = 53; + final val CATCH = 54; + final val FINALLY = 55; + final val WHILE = 56; + final val RETURN = 57; + final val MATCH = 58; + final val REQUIRES = 59; + + /** special symbols */ + final val COMMA = 61; + final val SEMI = 62; + final val DOT = 63; + final val USCORE = 64; + final val COLON = 65; + final val EQUALS = 66; + final val LARROW = 67; + final val ARROW = 68; + final val NEWLINE = 69; + final val SUBTYPE = 70; + final val SUPERTYPE = 71; + final val HASH = 72; + final val AT = 73; + final val VIEWBOUND = 74; + + /** parenthesis */ + final val LPAREN = 90; + final val RPAREN = 91; + final val LBRACKET = 92; + final val RBRACKET = 93; + final val LBRACE = 94; + final val RBRACE = 95; + + /** XML mode */ + final val XMLSTART = 96; +} diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala new file mode 100644 index 0000000000..70355350a7 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -0,0 +1,289 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +import symtab.Flags._; +import util.ListBuffer; + +abstract class TreeBuilder { + + val global: Global; + import global._; + import posAssigner.atPos; + + def freshName(prefix: String): Name; + + def freshName(): Name = freshName("x$"); + + private object patvarTransformer extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Ident(name) if (treeInfo.isVariableName(name) && name != nme.WILDCARD) => + atPos(tree.pos)(Bind(name, Ident(nme.WILDCARD))) + case Typed(id @ Ident(name), tpt) if (treeInfo.isVariableName(name) && name != nme.WILDCARD) => + Bind(name, atPos(tree.pos)(Typed(Ident(nme.WILDCARD), tpt))) setPos id.pos + case Apply(fn @ Apply(_, _), args) => + copy.Apply(tree, transform(fn), transformTrees(args)) + case Apply(fn, args) => + copy.Apply(tree, fn, transformTrees(args)) + case Typed(expr, tpt) => + copy.Typed(tree, transform(expr), tpt) + case Bind(name, body) => + copy.Bind(tree, name, transform(body)) + case Sequence(_) | Alternative(_) | Star(_) => + super.transform(tree) + case _ => + tree + } + } + + /** Traverse pattern and collect all variable names in buffer */ + private object getvarTraverser extends Traverser { + val buf = new ListBuffer[Name]; + def init: Traverser = { buf.clear; this } + override def traverse(tree: Tree): unit = tree match { + case Bind(name, tpe) => + if ((name != nme.WILDCARD) && (buf.elements forall (name !=))) buf += name; + traverse(tpe) + case _ => super.traverse(tree) + } + } + + /** Returns list of all pattern variables without duplicates */ + private def getVariables(tree: Tree): List[Name] = { + getvarTraverser.init.traverse(tree); + getvarTraverser.buf.toList + } + + private def mkTuple(trees: List[Tree]): Tree = trees match { + case List() => Literal(()) + case List(tree) => tree + case _ => Apply(Select(Ident(nme.scala_), newTermName("Tuple" + trees.length)), trees) + } + + /** If tree is a variable pattern, return Some("its name and type"). + * Otherwise return none */ + private def matchVarPattern(tree: Tree): Option[Pair[Name, Tree]] = tree match { + case Ident(name) => Some(Pair(name, TypeTree())) + case Bind(name, Ident(nme.WILDCARD)) => Some(Pair(name, TypeTree())) + case Typed(Ident(name), tpt) => Some(Pair(name, tpt)) + case Bind(name, Typed(Ident(nme.WILDCARD), tpt)) => Some(Pair(name, tpt)) + case _ => None + } + + /** Create tree representing (unencoded) binary operation expression or pattern. */ + def makeBinop(isExpr: boolean, left: Tree, op: Name, right: Tree): Tree = { + if (isExpr) { + if (treeInfo.isLeftAssoc(op)) { + Apply(Select(left, op.encode), List(right)) + } else { + val x = freshName(); + Block( + List(ValDef(Modifiers(SYNTHETIC), x, TypeTree(), left)), + Apply(Select(right, op.encode), List(Ident(x)))) + } + } else { + Apply(Ident(op.encode.toTypeName), List(left, right)) + } + } + + /** Create tree representing an object creation <new parents { stats }> */ + def makeNew(parents: List[Tree], stats: List[Tree], argss: List[List[Tree]]): Tree = + if (parents.tail.isEmpty && stats.isEmpty) + New(parents.head, argss) + else { + val x = nme.ANON_CLASS_NAME.toTypeName; + Block( + List(ClassDef( + Modifiers(FINAL | SYNTHETIC), x, List(), TypeTree(), + Template(parents, List(List()), argss, stats))), + New(Ident(x), List(List()))) + } + + /** Create a tree represeting an assignment <lhs = rhs> */ + def makeAssign(lhs: Tree, rhs: Tree): Tree = lhs match { + case Apply(fn, args) => Apply(Select(fn, nme.update), args ::: List(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, List())) + } + + /** Create tree representing a while loop */ + def makeWhile(lname: Name, cond: Tree, body: Tree): Tree = { + val continu = Apply(Ident(lname), List()); + val rhs = If(cond, Block(List(body), continu), Literal(())); + LabelDef(lname, Nil, rhs) + } + + /** Create tree representing a do-while loop */ + def makeDoWhile(lname: Name, body: Tree, cond: Tree): Tree = { + val continu = Apply(Ident(lname), List()); + val rhs = Block(List(body), If(cond, continu, Literal(()))); + LabelDef(lname, Nil, rhs) + } + + /** Create block of statements `stats' */ + def makeBlock(stats: List[Tree]): Tree = { + if (stats.isEmpty) Literal(()) + else if (!stats.last.isTerm) Block(stats, Literal(())); + else if (stats.length == 1) stats(0) + else Block(stats.init, stats.last) + } + + /** Create tree for for-comprehension generator <val pat0 <- rhs0> */ + def makeGenerator(pat: Tree, rhs: Tree): Tree = { + val pat1 = patvarTransformer.transform(pat); + val rhs1 = matchVarPattern(pat1) match { + case Some(_) => + rhs + case None => + Apply( + Select(rhs, nme.filter), + List(makeVisitor(List( + CaseDef(pat1.duplicate, EmptyTree, Literal(true)), + CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(false)))))) + } + CaseDef(pat1, EmptyTree, rhs1) + } + + /** 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. + */ + private def makeFor(mapName: Name, flatMapName: Name, enums: List[Tree], body: Tree): Tree = { + + def makeCont(pat: Tree, body: Tree): Tree = matchVarPattern(pat) match { + case Some(Pair(name, tpt)) => + Function(List(ValDef(Modifiers(PARAM), name, tpt, EmptyTree)), body) + case None => + makeVisitor(List(CaseDef(pat, EmptyTree, body))) + } + + def makeBind(meth: Name, qual: Tree, pat: Tree, body: Tree): Tree = + Apply(Select(qual, meth), List(makeCont(pat, body))); + + atPos(enums.head.pos) { + enums match { + case CaseDef(pat, g, rhs) :: Nil => + makeBind(mapName, rhs, pat, body) + case CaseDef(pat, g, rhs) :: (rest @ (CaseDef(_, _, _) :: _)) => + makeBind(flatMapName, rhs, pat, makeFor(mapName, flatMapName, rest, body)) + case CaseDef(pat, g, rhs) :: test :: rest => + makeFor(mapName, flatMapName, + CaseDef(pat, g, makeBind(nme.filter, rhs, pat.duplicate, test)) :: rest, + body) + } + } + } + + /** Create tree for for-do comprehension <for (enums) body> */ + def makeFor(enums: List[Tree], body: Tree): Tree = + makeFor(nme.foreach, nme.foreach, enums, body); + + /** Create tree for for-yield comprehension <for (enums) yield body> */ + def makeForYield(enums: List[Tree], body: Tree): Tree = + makeFor(nme.map, nme.flatMap, enums, body); + + /** Create tree for a pattern alternative */ + def makeAlternative(ts: List[Tree]): Tree = { + def alternatives(t: Tree): List[Tree] = t match { + case Alternative(ts) => ts + case _ => List(t) + } + Alternative(for (val t <- ts; val a <- alternatives(t)) yield a) + } + + /** Create tree for a pattern sequence */ + def makeSequence(ts: List[Tree]): Tree = { + def elements(t: Tree): List[Tree] = t match { + case Sequence(ts) => ts + case _ => List(t) + } + Sequence(for (val t <- ts; val e <- elements(t)) yield e) + } + + /** Create tree for the p+ regex pattern, becomes p p* */ + def makePlus(p: Tree): Tree = + makeSequence(List(p, Star(p.duplicate))); + + /** Create tree for the p? regex pattern, becomes (p| ) */ + def makeOpt(p: Tree): Tree = + makeAlternative(List(p, Sequence(List()))); + + /** Create visitor <x => x match cases> */ + def makeVisitor(cases: List[CaseDef]): Tree = { + val x = freshName(); + Function(List(ValDef(Modifiers(PARAM | SYNTHETIC), x, TypeTree(), EmptyTree)), Match(Ident(x), 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 <mods val pat0 = rhs> */ + def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree): List[Tree] = matchVarPattern(pat) match { + case Some(Pair(name, tpt)) => + List(ValDef(mods, name, tpt, rhs)) + + case None => + // in case there are no variables in pattern + // val p = e ==> e.match (case p => ()) + // + // in case there is exactly one variable in pattern + // val x_1 = e.match (case p => (x_1)) + // + // in case there are more variables in pattern + // val p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) + // val x_1 = t$._1 + // ... + // val x_N = t$._N + val pat1 = patvarTransformer.transform(pat); + val vars = getVariables(pat1); + val matchExpr = atPos(pat1.pos){ + Match(rhs, List(CaseDef(pat1, EmptyTree, mkTuple(vars map Ident)))) + } + vars match { + case List() => + List(matchExpr) + case List(vname) => + List(ValDef(mods, vname, TypeTree(), matchExpr)) + case _ => + val tmp = freshName(); + val firstDef = ValDef(Modifiers(PRIVATE | LOCAL | SYNTHETIC), tmp, TypeTree(), matchExpr); + var cnt = 0; + val restDefs = for (val v <- vars) yield { + cnt = cnt + 1; + ValDef(mods, v, TypeTree(), Select(Ident(tmp), newTermName("_" + cnt))) + } + firstDef :: restDefs + } + } + + /** Create a tree representing a function type */ + def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree( + Select(Ident(nme.scala_), newTypeName("Function" + argtpes.length)), + argtpes ::: List(restpe)); + + /** Append implicit view section if for `implicitViews' if nonempty */ + def addImplicitViews(owner: Name, vparamss: List[List[ValDef]], implicitViews: List[Tree]): List[List[ValDef]] = { + val mods = Modifiers(if (owner.isTypeName) PARAMACCESSOR | LOCAL | PRIVATE else PARAM); + def makeViewParam(tpt: Tree) = ValDef(mods | IMPLICIT, freshName("view$"), tpt, EmptyTree); + if (implicitViews.isEmpty) vparamss + else vparamss ::: List(implicitViews map makeViewParam) + } + + /** Create a tree representing a packaging */ + def makePackaging(pkg: Tree, stats: List[Tree]): PackageDef = pkg match { + case Ident(name) => + PackageDef(name, stats) + case Select(qual, name) => + makePackaging(qual, List(PackageDef(name, stats))) + } +} diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala new file mode 100644 index 0000000000..91dad33b05 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala @@ -0,0 +1,736 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend; + +import scala.tools.nsc.backend.icode._; + +import scala.collection.mutable.{Map, HashMap}; + +/** + * Scala primitive operations are represented as methods in Any and + * AnyVal subclasses. Here we demultiplex them by providing a mapping + * from their symbols to integers. Different methods exist for + * different value types, but with the same meaning (like plus, minus, + * etc.). They will all be mapped to the same int. + * + * Note: The three equal methods have the following semantics: + * - "==" checks for null, and if non-null, calls java.lang.Object.equals + * (class: Any; modifier: final). Primitive: EQ + * - "eq" usual reference comparison + * (class: AnyRef; modifier: final). Primitive: ID + * - "equals" user-defined equality (Java semantics) + * (class: Object; modifier: none). Primitive: EQUALS + * + * Inspired from the scalac compiler. + */ +abstract class ScalaPrimitives { + val global: Global; + + import global._; + import definitions._; + import global.icodes._; + + // Arithmetic unary operations + final val POS = 1; // +x + final val NEG = 2; // -x + final val NOT = 3; // ~x + + // Arithmetic binary operations + final val ADD = 10; // x + y + final val SUB = 11; // x - y + final val MUL = 12; // x * y + final val DIV = 13; // x / y + final val MOD = 14; // x % y + + // Bitwise operations + final val OR = 20; // x | y + final val XOR = 21; // x ^ y + final val AND = 22; // x & y + + // Shift operations + final val LSL = 30; // x << y + final val LSR = 31; // x >>> y + final val ASR = 32; // x >> y + + // Comparison operations + final val ID = 40; // x eq y + final val NI = 41; // x ne y + final val EQ = 42; // x == y + final val NE = 43; // x != y + final val LT = 44; // x < y + final val LE = 45; // x <= y + final val GE = 46; // x > y + final val GT = 47; // x >= y + + // Boolean unary operations + final val ZNOT = 50; // !x + + // Boolean binary operations + final val ZOR = 60; // x || y + final val ZAND = 61; // x && y + + // Array operations + final val LENGTH = 70; // x.length + final val APPLY = 71; // x(y) + final val UPDATE = 72; // x(y) = z + + // Any operations + final val IS = 80; // x.is[y] + final val AS = 81; // x.as[y] + final val EQUALS = 82; // x.equals(y) + final val HASHCODE = 83; // x.hashcode() + final val TOSTRING = 84; // x.toString() + final val ISERASED = 85; // x.is$erased[y] + final val ASERASED = 86; // x.as$erased[y] + + // AnyRef operations + final val SYNCHRONIZED = 90; // x.synchronized(y) + + // String operations + final val CONCAT = 100; // String.valueOf(x)+String.valueOf(y) + + // coercions + final val COERCE = 101; + + // RunTime operations + final val BOX = 110; // RunTime.box_<X>(x) + final val UNBOX = 111; // RunTime.unbox_<X>(x) + final val NEW_ZARRAY = 112; // RunTime.zarray(x) + final val NEW_BARRAY = 113; // RunTime.barray(x) + final val NEW_SARRAY = 114; // RunTime.sarray(x) + final val NEW_CARRAY = 115; // RunTime.carray(x) + final val NEW_IARRAY = 116; // RunTime.iarray(x) + final val NEW_LARRAY = 117; // RunTime.larray(x) + final val NEW_FARRAY = 118; // RunTime.farray(x) + final val NEW_DARRAY = 119; // RunTime.darray(x) + final val NEW_OARRAY = 120; // RunTime.oarray(x) + + final val ZARRAY_LENGTH = 131; // RunTime.zarray_length(x) + final val BARRAY_LENGTH = 132; // RunTime.barray_length(x) + final val SARRAY_LENGTH = 133; // RunTime.sarray_length(x) + final val CARRAY_LENGTH = 134; // RunTime.carray_length(x) + final val IARRAY_LENGTH = 135; // RunTime.iarray_length(x) + final val LARRAY_LENGTH = 136; // RunTime.larray_length(x) + final val FARRAY_LENGTH = 137; // RunTime.farray_length(x) + final val DARRAY_LENGTH = 138; // RunTime.darray_length(x) + final val OARRAY_LENGTH = 139; // RunTime.oarray_length(x) + + final val ZARRAY_GET = 140; // RunTime.zarray_get(x,y) + final val BARRAY_GET = 141; // RunTime.barray_get(x,y) + final val SARRAY_GET = 142; // RunTime.sarray_get(x,y) + final val CARRAY_GET = 143; // RunTime.carray_get(x,y) + final val IARRAY_GET = 144; // RunTime.iarray_get(x,y) + final val LARRAY_GET = 145; // RunTime.larray_get(x,y) + final val FARRAY_GET = 146; // RunTime.farray_get(x,y) + final val DARRAY_GET = 147; // RunTime.darray_get(x,y) + final val OARRAY_GET = 148; // RunTime.oarray_get(x,y) + + final val ZARRAY_SET = 150; // RunTime.zarray(x,y,z) + final val BARRAY_SET = 151; // RunTime.barray(x,y,z) + final val SARRAY_SET = 152; // RunTime.sarray(x,y,z) + final val CARRAY_SET = 153; // RunTime.carray(x,y,z) + final val IARRAY_SET = 154; // RunTime.iarray(x,y,z) + final val LARRAY_SET = 155; // RunTime.larray(x,y,z) + final val FARRAY_SET = 156; // RunTime.farray(x,y,z) + final val DARRAY_SET = 157; // RunTime.darray(x,y,z) + final val OARRAY_SET = 158; // RunTime.oarray(x,y,z) + + final val B2B = 200; // RunTime.b2b(x) + final val B2S = 201; // RunTime.b2s(x) + final val B2C = 202; // RunTime.b2c(x) + final val B2I = 203; // RunTime.b2i(x) + final val B2L = 204; // RunTime.b2l(x) + final val B2F = 205; // RunTime.b2f(x) + final val B2D = 206; // RunTime.b2d(x) + + final val S2B = 210; // RunTime.s2b(x) + final val S2S = 211; // RunTime.s2s(x) + final val S2C = 212; // RunTime.s2c(x) + final val S2I = 213; // RunTime.s2i(x) + final val S2L = 214; // RunTime.s2l(x) + final val S2F = 215; // RunTime.s2f(x) + final val S2D = 216; // RunTime.s2d(x) + + final val C2B = 220; // RunTime.c2b(x) + final val C2S = 221; // RunTime.c2s(x) + final val C2C = 222; // RunTime.c2c(x) + final val C2I = 223; // RunTime.c2i(x) + final val C2L = 224; // RunTime.c2l(x) + final val C2F = 225; // RunTime.c2f(x) + final val C2D = 226; // RunTime.c2d(x) + + final val I2B = 230; // RunTime.i2b(x) + final val I2S = 231; // RunTime.i2s(x) + final val I2C = 232; // RunTime.i2c(x) + final val I2I = 233; // RunTime.i2i(x) + final val I2L = 234; // RunTime.i2l(x) + final val I2F = 235; // RunTime.i2f(x) + final val I2D = 236; // RunTime.i2d(x) + + final val L2B = 240; // RunTime.l2b(x) + final val L2S = 241; // RunTime.l2s(x) + final val L2C = 242; // RunTime.l2c(x) + final val L2I = 243; // RunTime.l2i(x) + final val L2L = 244; // RunTime.l2l(x) + final val L2F = 245; // RunTime.l2f(x) + final val L2D = 246; // RunTime.l2d(x) + + final val F2B = 250; // RunTime.f2b(x) + final val F2S = 251; // RunTime.f2s(x) + final val F2C = 252; // RunTime.f2c(x) + final val F2I = 253; // RunTime.f2i(x) + final val F2L = 254; // RunTime.f2l(x) + final val F2F = 255; // RunTime.f2f(x) + final val F2D = 256; // RunTime.f2d(x) + + final val D2B = 260; // RunTime.d2b(x) + final val D2S = 261; // RunTime.d2s(x) + final val D2C = 262; // RunTime.d2c(x) + final val D2I = 263; // RunTime.d2i(x) + final val D2L = 264; // RunTime.d2l(x) + final val D2F = 265; // RunTime.d2f(x) + final val D2D = 266; // RunTime.d2d(x) + + + private var primitives: Map[Symbol, Int] = _; + + /** Initialize the primitive map */ + def init: Unit = { + primitives = new HashMap(); + + // scala.Any + addPrimitive(Any_==, EQ); + addPrimitive(Any_!=, NE); + addPrimitive(Any_equals, EQUALS); + addPrimitive(Any_hashCode, HASHCODE); + addPrimitive(Any_toString, TOSTRING); + addPrimitive(Any_isInstanceOf, IS); + addPrimitive(Any_asInstanceOf, AS); + addPrimitive(Any_isInstanceOfErased, ISERASED); + addPrimitive(Any_asInstanceOfErased, ASERASED); + + // java.lang.Object + addPrimitive(Object_eq, ID); + addPrimitive(Object_ne, NI); + addPrimitive(Object_==, EQ); + addPrimitive(Object_!=, NE); + addPrimitive(Object_synchronized, SYNCHRONIZED); + addPrimitive(Object_isInstanceOf, IS); + addPrimitive(Object_asInstanceOf, AS); + addPrimitive(Object_equals, EQUALS); + addPrimitive(Object_hashCode, HASHCODE); + addPrimitive(Object_toString, TOSTRING); + + // java.lang.String + addPrimitive(String_+, CONCAT); + + // scala.Array + addPrimitives(ArrayClass, nme.length, LENGTH); + addPrimitives(ArrayClass, nme.apply, APPLY); + addPrimitives(ArrayClass, nme.update, UPDATE); + + // scala.Unit + addPrimitives(UnitClass, nme.EQ, EQ); + addPrimitives(UnitClass, nme.NE, NE); + addPrimitives(UnitClass, nme.equals_, EQUALS); + addPrimitives(UnitClass, nme.hashCode_, HASHCODE); + addPrimitives(UnitClass, nme.toString_, TOSTRING); + + // scala.Boolean + addPrimitives(BooleanClass, nme.EQ, EQ); + addPrimitives(BooleanClass, nme.NE, NE); + addPrimitives(BooleanClass, nme.equals_, EQUALS); + addPrimitives(BooleanClass, nme.hashCode_, HASHCODE); + addPrimitives(BooleanClass, nme.toString_, TOSTRING); + addPrimitive(Boolean_not, ZNOT); + addPrimitive(Boolean_or, ZOR); + addPrimitive(Boolean_and, ZAND); + addPrimitives(BooleanClass, nme.OR, OR); + addPrimitives(BooleanClass, nme.AND, AND); + addPrimitives(BooleanClass, nme.XOR, XOR); + addPrimitives(BooleanClass, nme.ADD, CONCAT); + + // scala.Byte + addCoercions(ByteClass); + addPrimitives(ByteClass, nme.EQ, EQ); + addPrimitives(ByteClass, nme.NE, NE); + addPrimitives(ByteClass, nme.equals_, EQUALS); + addPrimitives(ByteClass, nme.hashCode_, HASHCODE); + addPrimitives(ByteClass, nme.toString_, TOSTRING); + addPrimitives(ByteClass, nme.NOT, NOT); + addPrimitives(ByteClass, nme.ADD, ADD); + addPrimitives(ByteClass, nme.SUB, SUB); + addPrimitives(ByteClass, nme.MUL, MUL); + addPrimitives(ByteClass, nme.DIV, DIV); + addPrimitives(ByteClass, nme.MOD, MOD); + addPrimitives(ByteClass, nme.LT, LT); + addPrimitives(ByteClass, nme.LE, LE); + addPrimitives(ByteClass, nme.GT, GT); + addPrimitives(ByteClass, nme.GE, GE); + addPrimitives(ByteClass, nme.XOR, XOR); + addPrimitives(ByteClass, nme.OR, OR); + addPrimitives(ByteClass, nme.AND, AND); + addPrimitives(ByteClass, nme.LSL, LSL); + addPrimitives(ByteClass, nme.LSR, LSR); + addPrimitives(ByteClass, nme.ASR, ASR); + // conversions + addPrimitives(ByteClass, nme.toByte, B2B); + addPrimitives(ByteClass, nme.toShort, B2S); + addPrimitives(ByteClass, nme.toChar, B2C); + addPrimitives(ByteClass, nme.toInt, B2I); + addPrimitives(ByteClass, nme.toLong, B2L); + addPrimitives(ByteClass, nme.toFloat, B2F); + addPrimitives(ByteClass, nme.toDouble, B2D); + + // scala.Short + addCoercions(ShortClass); + addPrimitives(ShortClass, nme.EQ, EQ); + addPrimitives(ShortClass, nme.NE, NE); + addPrimitives(ShortClass, nme.equals_, EQUALS); + addPrimitives(ShortClass, nme.hashCode_, HASHCODE); + addPrimitives(ShortClass, nme.toString_, TOSTRING); + addPrimitives(ShortClass, nme.NOT, NOT); + addPrimitives(ShortClass, nme.ADD, ADD); + addPrimitives(ShortClass, nme.SUB, SUB); + addPrimitives(ShortClass, nme.MUL, MUL); + addPrimitives(ShortClass, nme.DIV, DIV); + addPrimitives(ShortClass, nme.MOD, MOD); + addPrimitives(ShortClass, nme.LT, LT); + addPrimitives(ShortClass, nme.LE, LE); + addPrimitives(ShortClass, nme.GT, GT); + addPrimitives(ShortClass, nme.GE, GE); + addPrimitives(ShortClass, nme.XOR, XOR); + addPrimitives(ShortClass, nme.OR, OR); + addPrimitives(ShortClass, nme.AND, AND); + addPrimitives(ShortClass, nme.LSL, LSL); + addPrimitives(ShortClass, nme.LSR, LSR); + addPrimitives(ShortClass, nme.ASR, ASR); + // conversions + addPrimitives(ShortClass, nme.toByte, S2B); + addPrimitives(ShortClass, nme.toShort, S2S); + addPrimitives(ShortClass, nme.toChar, S2C); + addPrimitives(ShortClass, nme.toInt, S2I); + addPrimitives(ShortClass, nme.toLong, S2L); + addPrimitives(ShortClass, nme.toFloat, S2F); + addPrimitives(ShortClass, nme.toDouble, S2D); + + // scala.Char + addCoercions(CharClass); + addPrimitives(CharClass, nme.EQ, EQ); + addPrimitives(CharClass, nme.NE, NE); + addPrimitives(CharClass, nme.equals_, EQUALS); + addPrimitives(CharClass, nme.hashCode_, HASHCODE); + addPrimitives(CharClass, nme.toString_, TOSTRING); + addPrimitives(CharClass, nme.NOT, NOT); + addPrimitives(CharClass, nme.ADD, ADD); + addPrimitives(CharClass, nme.SUB, SUB); + addPrimitives(CharClass, nme.MUL, MUL); + addPrimitives(CharClass, nme.DIV, DIV); + addPrimitives(CharClass, nme.MOD, MOD); + addPrimitives(CharClass, nme.LT, LT); + addPrimitives(CharClass, nme.LE, LE); + addPrimitives(CharClass, nme.GT, GT); + addPrimitives(CharClass, nme.GE, GE); + addPrimitives(CharClass, nme.XOR, XOR); + addPrimitives(CharClass, nme.OR, OR); + addPrimitives(CharClass, nme.AND, AND); + addPrimitives(CharClass, nme.LSL, LSL); + addPrimitives(CharClass, nme.LSR, LSR); + addPrimitives(CharClass, nme.ASR, ASR); + // conversions + addPrimitives(CharClass, nme.toByte, C2B); + addPrimitives(CharClass, nme.toShort, C2S); + addPrimitives(CharClass, nme.toChar, C2C); + addPrimitives(CharClass, nme.toInt, C2I); + addPrimitives(CharClass, nme.toLong, C2L); + addPrimitives(CharClass, nme.toFloat, C2F); + addPrimitives(CharClass, nme.toDouble, C2D); + + // scala.Int + addCoercions(IntClass); + addPrimitives(IntClass, nme.EQ, EQ); + addPrimitives(IntClass, nme.NE, NE); + addPrimitives(IntClass, nme.equals_, EQUALS); + addPrimitives(IntClass, nme.hashCode_, HASHCODE); + addPrimitives(IntClass, nme.toString_, TOSTRING); + addPrimitives(IntClass, nme.NOT, NOT); + addPrimitives(IntClass, nme.ADD, ADD); + addPrimitives(IntClass, nme.SUB, SUB); + addPrimitives(IntClass, nme.MUL, MUL); + addPrimitives(IntClass, nme.DIV, DIV); + addPrimitives(IntClass, nme.MOD, MOD); + addPrimitives(IntClass, nme.LT, LT); + addPrimitives(IntClass, nme.LE, LE); + addPrimitives(IntClass, nme.GT, GT); + addPrimitives(IntClass, nme.GE, GE); + addPrimitives(IntClass, nme.XOR, XOR); + addPrimitives(IntClass, nme.OR, OR); + addPrimitives(IntClass, nme.AND, AND); + addPrimitives(IntClass, nme.LSL, LSL); + addPrimitives(IntClass, nme.LSR, LSR); + addPrimitives(IntClass, nme.ASR, ASR); + // conversions + addPrimitives(IntClass, nme.toByte, I2B); + addPrimitives(IntClass, nme.toShort, I2S); + addPrimitives(IntClass, nme.toChar, I2C); + addPrimitives(IntClass, nme.toInt, I2I); + addPrimitives(IntClass, nme.toLong, I2L); + addPrimitives(IntClass, nme.toFloat, I2F); + addPrimitives(IntClass, nme.toDouble, I2D); + + // scala.Long + addCoercions(LongClass); + addPrimitives(LongClass, nme.EQ, EQ); + addPrimitives(LongClass, nme.NE, NE); + addPrimitives(LongClass, nme.equals_, EQUALS); + addPrimitives(LongClass, nme.hashCode_, HASHCODE); + addPrimitives(LongClass, nme.toString_, TOSTRING); + addPrimitives(LongClass, nme.NOT, NOT); + addPrimitives(LongClass, nme.ADD, ADD); + addPrimitives(LongClass, nme.SUB, SUB); + addPrimitives(LongClass, nme.MUL, MUL); + addPrimitives(LongClass, nme.DIV, DIV); + addPrimitives(LongClass, nme.MOD, MOD); + addPrimitives(LongClass, nme.LT, LT); + addPrimitives(LongClass, nme.LE, LE); + addPrimitives(LongClass, nme.GT, GT); + addPrimitives(LongClass, nme.GE, GE); + addPrimitives(LongClass, nme.XOR, XOR); + addPrimitives(LongClass, nme.OR, OR); + addPrimitives(LongClass, nme.AND, AND); + addPrimitives(LongClass, nme.LSL, LSL); + addPrimitives(LongClass, nme.LSR, LSR); + addPrimitives(LongClass, nme.ASR, ASR); + // conversions + addPrimitives(LongClass, nme.toByte, L2B); + addPrimitives(LongClass, nme.toShort, L2S); + addPrimitives(LongClass, nme.toChar, L2C); + addPrimitives(LongClass, nme.toInt, L2I); + addPrimitives(LongClass, nme.toLong, L2L); + addPrimitives(LongClass, nme.toFloat, L2F); + addPrimitives(LongClass, nme.toDouble, L2D); + + // scala.Float + addCoercion(FloatClass); + addPrimitives(FloatClass, nme.EQ, EQ); + addPrimitives(FloatClass, nme.NE, NE); + addPrimitives(FloatClass, nme.equals_, EQUALS); + addPrimitives(FloatClass, nme.hashCode_, HASHCODE); + addPrimitives(FloatClass, nme.toString_, TOSTRING); + addPrimitives(FloatClass, nme.ADD, ADD); + addPrimitives(FloatClass, nme.SUB, SUB); + addPrimitives(FloatClass, nme.MUL, MUL); + addPrimitives(FloatClass, nme.DIV, DIV); + addPrimitives(FloatClass, nme.MOD, MOD); + addPrimitives(FloatClass, nme.LT, LT); + addPrimitives(FloatClass, nme.LE, LE); + addPrimitives(FloatClass, nme.GT, GT); + addPrimitives(FloatClass, nme.GE, GE); + // conversions + addPrimitives(FloatClass, nme.toByte, F2B); + addPrimitives(FloatClass, nme.toShort, F2S); + addPrimitives(FloatClass, nme.toChar, F2C); + addPrimitives(FloatClass, nme.toInt, F2I); + addPrimitives(FloatClass, nme.toLong, F2L); + addPrimitives(FloatClass, nme.toFloat, F2F); + addPrimitives(FloatClass, nme.toDouble, F2D); + + // scala.Double + addPrimitives(DoubleClass, nme.EQ, EQ); + addPrimitives(DoubleClass, nme.NE, NE); + addPrimitives(DoubleClass, nme.equals_, EQUALS); + addPrimitives(DoubleClass, nme.hashCode_, HASHCODE); + addPrimitives(DoubleClass, nme.toString_, TOSTRING); + addPrimitives(DoubleClass, nme.ADD, ADD); + addPrimitives(DoubleClass, nme.SUB, SUB); + addPrimitives(DoubleClass, nme.MUL, MUL); + addPrimitives(DoubleClass, nme.DIV, DIV); + addPrimitives(DoubleClass, nme.MOD, MOD); + addPrimitives(DoubleClass, nme.LT, LT); + addPrimitives(DoubleClass, nme.LE, LE); + addPrimitives(DoubleClass, nme.GT, GT); + addPrimitives(DoubleClass, nme.GE, GE); + // conversions + addPrimitives(DoubleClass, nme.toByte, D2B); + addPrimitives(DoubleClass, nme.toShort, D2S); + addPrimitives(DoubleClass, nme.toChar, D2C); + addPrimitives(DoubleClass, nme.toInt, D2I); + addPrimitives(DoubleClass, nme.toLong, D2L); + addPrimitives(DoubleClass, nme.toFloat, D2F); + addPrimitives(DoubleClass, nme.toDouble, D2D); + + // and the type map! + initPrimitiveTypeMap; + } + + /** Add a primitive operation to the map */ + def addPrimitive(s: Symbol, code: Int): Unit = { + assert(!(primitives contains s), "Duplicate primitive " + s); + primitives += s -> code; + } + + def addPrimitives(cls: Symbol, method: Name, code: Int): Unit = { + val tpe = cls.info; + val sym = tpe.member(method); + sym.info match { + case OverloadedType(pre, alternatives) => + log("Adding " + alternatives.length + " overloads for " + sym.fullNameString); + code match { + case SUB => + alternatives foreach ((s) => + if (s.info.paramTypes.length == 0) + addPrimitive(s, NEG); // unary + else + addPrimitive(s, code)); + + case ADD => + alternatives foreach ((s) => + if (s.info.paramTypes.length == 0) + addPrimitive(s, POS); // unary + else if (s.info.paramTypes.head == definitions.StringClass.tpe) + addPrimitive(s, CONCAT); // string concatenation + else + addPrimitive(s, code)); + + case _ => + alternatives foreach ((s) => addPrimitive(s, code)); + } + + case _ => + addPrimitive(sym, code); + } + } + + def addCoercion(cls: Symbol) = { + assert(cls == FloatClass, + "Only scala.Double has non-overloaded 'coerce'"); + val method = cls.info.member(nme.coerce); + addPrimitive(method, F2D); + } + + def addCoercions(cls: Symbol): Unit = { + val OverloadedType(_, coercions) = cls.info.member(nme.coerce).info; + if (cls == ByteClass) + coercions foreach ((m) => + if (m.info.resultType == ShortClass.tpe) + addPrimitive(m, B2S) + else if (m.info.resultType == IntClass.tpe) + addPrimitive(m, B2I) + else if (m.info.resultType == LongClass.tpe) + addPrimitive(m, B2L) + else if (m.info.resultType == FloatClass.tpe) + addPrimitive(m, B2F) + else if (m.info.resultType == DoubleClass.tpe) + addPrimitive(m, B2D) + else + abort("Unknown coercion method: " + m.info) + ) + else if (cls == ShortClass) + coercions foreach ((m) => + if (m.info.resultType == IntClass.tpe) + addPrimitive(m, S2I) + else if (m.info.resultType == LongClass.tpe) + addPrimitive(m, S2L) + else if (m.info.resultType == FloatClass.tpe) + addPrimitive(m, S2F) + else if (m.info.resultType == DoubleClass.tpe) + addPrimitive(m, S2D) + else + abort("Unknown coercion method: " + m.fullNameString) + ) + else if (cls == CharClass) + coercions foreach ((m) => + if (m.info.resultType == IntClass.tpe) + addPrimitive(m, C2I) + else if (m.info.resultType == LongClass.tpe) + addPrimitive(m, C2L) + else if (m.info.resultType == FloatClass.tpe) + addPrimitive(m, C2F) + else if (m.info.resultType == DoubleClass.tpe) + addPrimitive(m, C2D) + else + abort("Unknown coercion method: " + m.fullNameString) + ) + else if (cls == IntClass) + coercions foreach ((m) => + if (m.info.resultType == LongClass.tpe) + addPrimitive(m, I2L) + else if (m.info.resultType == FloatClass.tpe) + addPrimitive(m, I2F) + else if (m.info.resultType == DoubleClass.tpe) + addPrimitive(m, I2D) + else + abort("Unknown coercion method: " + m.fullNameString) + ) + else if (cls == LongClass) + coercions foreach ((m) => + if (m.info.resultType == FloatClass.tpe) + addPrimitive(m, L2F) + else if (m.info.resultType == DoubleClass.tpe) + addPrimitive(m, L2D) + else + abort("Unknown coercion method: " + m.fullNameString) + ) + else if (cls == FloatClass) + coercions foreach ((m) => + if (m.info.resultType == DoubleClass.tpe) + addPrimitive(m, F2D) + else + abort("Unknown coercion method: " + m.fullNameString) + ) + else + abort("Unknown value type: " + cls.fullNameString); + } + + def isCoercion(code: Int): Boolean = (code >= B2B) && (code <= D2D); + + /** Check whether the given operation code is an array operation. */ + def isArrayOp(code: Int): Boolean = + isArrayNew(code) | isArrayLength(code) | isArrayGet(code) | isArraySet(code); + + def isArrayNew(code: Int): Boolean = code match { + case NEW_ZARRAY | NEW_BARRAY | NEW_SARRAY | NEW_CARRAY | + NEW_IARRAY | NEW_LARRAY | NEW_FARRAY | NEW_DARRAY | + NEW_OARRAY => true; + case _ => false; + } + + def isArrayLength(code: Int): Boolean = code match { + case ZARRAY_LENGTH | BARRAY_LENGTH | SARRAY_LENGTH | CARRAY_LENGTH | + IARRAY_LENGTH | LARRAY_LENGTH | FARRAY_LENGTH | DARRAY_LENGTH | + OARRAY_LENGTH | LENGTH => true; + case _ => false; + } + + def isArrayGet(code: Int): Boolean = code match { + case ZARRAY_GET | BARRAY_GET | SARRAY_GET | CARRAY_GET | + IARRAY_GET | LARRAY_GET | FARRAY_GET | DARRAY_GET | + OARRAY_GET | APPLY => true; + case _ => false; + } + + def isArraySet(code: Int): Boolean = code match { + case ZARRAY_SET | BARRAY_SET | SARRAY_SET | CARRAY_SET | + IARRAY_SET | LARRAY_SET | FARRAY_SET | DARRAY_SET | + OARRAY_SET | UPDATE => true; + case _ => false; + } + + /** Check whether the given code is a comparison operator */ + def isComparisonOp(code: Int): Boolean = code match { + case ID | NI | EQ | NE | + LT | LE | GT | GE => true; + + case _ => false; + } + + def isArithmeticOp(code: Int): Boolean = code match { + case POS | NEG | NOT => true; // unary + case ADD | SUB | MUL | + DIV | MOD => true; // binary + case OR | XOR | AND | + LSL | LSR | ASR => true; // bitwise + case _ => false; + } + + def isLogicalOp(code: Int): Boolean = code match { + case ZNOT | ZAND | ZOR => true; + case _ => false; + } + + def isShiftOp(code: Int): Boolean = code match { + case LSL | LSR | ASR => true; + case _ => false; + } + + def isBitwiseOp(code: Int): Boolean = code match { + case OR | XOR | AND => true; + case _ => false; + } + + def isPrimitive(sym: Symbol): Boolean = primitives contains sym; + + /** Return the code for the givem symbol. */ + def getPrimitive(sym: Symbol): Int = { + assert(isPrimitive(sym), "Unkown primitive " + sym); + primitives(sym); + } + + /** + * Return the primitive code of the given operation. If the + * operation is an array get/set, we inspect the type of the receiver + * to demux the operation. + * + * @param fun The method symbol + * @param tpe The type of the receiver object. It is used only for array + * operations + */ + def getPrimitive(fun: Symbol, tpe: Type): Int = { + import definitions._; + val code = getPrimitive(fun); + + var elem: Type = null; + tpe match { + case TypeRef(_, sym, _elem :: Nil) + if (sym == ArrayClass) => elem = _elem; + case _ => (); + } + + code match { + + case APPLY => + toTypeKind(elem) match { + case BOOL => ZARRAY_GET; + case BYTE => BARRAY_GET; + case SHORT => SARRAY_GET; + case CHAR => CARRAY_GET; + case INT => IARRAY_GET; + case LONG => LARRAY_GET; + case FLOAT => FARRAY_GET; + case DOUBLE => DARRAY_GET; + case REFERENCE(_) | ARRAY(_) => OARRAY_GET; + case _ => + abort("Unexpected array element type: " + elem); + } + + case UPDATE => + toTypeKind(elem) match { + case BOOL => ZARRAY_SET; + case BYTE => BARRAY_SET; + case SHORT => SARRAY_SET; + case CHAR => CARRAY_SET; + case INT => IARRAY_SET; + case LONG => LARRAY_SET; + case FLOAT => FARRAY_SET; + case DOUBLE => DARRAY_SET; + case REFERENCE(_) | ARRAY(_) => OARRAY_SET; + case _ => + abort("Unexpected array element type: " + elem); + } + + case LENGTH => + toTypeKind(elem) match { + case BOOL => ZARRAY_LENGTH; + case BYTE => BARRAY_LENGTH; + case SHORT => SARRAY_LENGTH; + case CHAR => CARRAY_LENGTH; + case INT => IARRAY_LENGTH; + case LONG => LARRAY_LENGTH; + case FLOAT => FARRAY_LENGTH; + case DOUBLE => DARRAY_LENGTH; + case REFERENCE(_) | ARRAY(_) => OARRAY_LENGTH; + case _ => + abort("Unexpected array element type: " + elem); + } + + case _ => + code; + } + } + +} diff --git a/src/compiler/scala/tools/nsc/backend/WorklistAlgorithm.scala b/src/compiler/scala/tools/nsc/backend/WorklistAlgorithm.scala new file mode 100644 index 0000000000..6676c820bc --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/WorklistAlgorithm.scala @@ -0,0 +1,50 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend; + +import scala.tools.nsc.ast._; +import scala.collection.mutable.MutableList; + +/** + * Simple implementation of a worklist algorithm. A processing + * function is applied repeatedly to the first element in the + * worklist, as long as the stack is not empty. + * + * The client class should mix-in this trait and initialize the + * worklist field and define the processElement method. Then call + * the 'run' method providing a function that initializes the + * worklist. + * + * @see scala.tools.nsc.backend.icode.Linearizers + */ +trait WorklistAlgorithm { + type Elem; + type WList <: MutableList[Elem]; + + val worklist: WList; + + /** + * Run the iterative algorithm until the worklist + * remains empty. The initializer is run once before + * the loop starts and should initialize the worklist. + */ + def run(initWorklist: => Unit) = { + initWorklist; + + while (!(worklist.length == 0)) + processElement(dequeue); + } + + /** + * Process the current element from the worklist. + */ + def processElement(e: Elem): Unit; + + /** Remove and return the first element to be processed from the worklist. */ + def dequeue: Elem; +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala new file mode 100644 index 0000000000..6fdbe98c57 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -0,0 +1,269 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.tools.nsc.ast._; +import scala.collection.mutable.Map; +import scala.tools.nsc.util.Position; + +trait BasicBlocks: ICodes { + import opcodes._; + + /** This class represents a basic block. Each + * basic block contains a list of instructions that are + * either executed all, or none. No jumps + * to/from the "middle" of the basic block are allowed. + */ + class BasicBlock (theLabel: int, val code: Code) { + + /** The type stack at the begining of the block */ + var initialStack : TypeStack = null; + + /** The label of the block */ + val label = theLabel; + + /** The substitute variables of the block + * in the case of a recursive method */ + var substituteVars : List[Symbol] = null; + + /** The stack at the end of the block */ + var endStack : TypeStack = null; + + /** When set, the 'emit' methods will be ignored. */ + var ignore: Boolean = false; + + var preds: List[BasicBlock] = null; + + /** ICode instructions, used as temporary storage while emitting code. + * Once closed is called, only the `instrs' array should be used. + */ + private var instructionList: List[Instruction] = Nil; + + private var _lastInstruction: Instruction = null; + + private var closed: boolean = false; + + private var instrs: Array[Instruction] = _; + + // public: + + /** Compute an hashCode for the block */ + override def hashCode() = label; + + /** Apply a function to all the instructions of the block. */ + def traverse(f: Instruction => unit) = { + assert(closed, "Traversing an open block!: "); + instrs foreach f; + } + + def traverseBackwards(f: Instruction => Unit) = { + var i = instrs.length - 1; + while (i >= 0) { + f(instrs(i)); + i = i - 1 + } + } + + /** The number of instructions in this basic block so far. */ + def size: Int = + if (isClosed) + instrs.length; + else + instructionList.length; + + /** Initialize the stack of the block, must be done before evaluation + * the type stack */ + def initStack(stack : TypeStack) = { + if (initialStack == null) { + initialStack = stack; + endStack = null; + } + } + + ///////////////////// Substitutions /////////////////////// + + /** + * Replace the instruction at the given position. Used by labels when + * they are anchored. + */ + def replaceInstruction(pos: Int, instr: Instruction): Boolean = { + assert(closed, "Instructions can be replaced only after the basic block is closed"); + + instrs(pos) = instr; + true + } + + /** + * Replace the given instruction with the new one. + * Returns `true' if it actually changed something. + */ + def replaceInstruction(oldInstr: Instruction, newInstr: Instruction): Boolean = { + assert(closed, "Instructions can be replaced only after the basic block is closed"); + + var i = 0; + var changed = false; + while (i < instrs.length && !changed) { + if (instrs(i) == oldInstr) { + instrs(i) = newInstr; + changed = true; + } + i = i + 1; + } + changed + } + + /** Replace all instructions found in the map. */ + def subst(map: Map[Instruction, Instruction]) = + if (!closed) substOnList(map) else { + var i = 0; + while (i < instrs.length) { + map get instrs(i) match { + case Some(instr) => replaceInstruction(i, instr); + case None => (); + } + i = i + 1; + } + } + + private def substOnList(map: Map[Instruction, Instruction]): Unit = { + def subst(l: List[Instruction]): List[Instruction] = l match { + case Nil => Nil + case x :: xs => + map.get(x) match { + case Some(newInstr) => newInstr :: subst(xs); + case None => x :: subst(xs); + } + } + + instructionList = subst(instructionList); + } + + ////////////////////// Emit ////////////////////// + + + /** Add a new instruction at the end of the block, + * using the same source position as the last emitted instruction + */ + def emit(instr: Instruction): Unit = { + if (!instructionList.isEmpty) + emit(instr, instructionList.head.pos); + else + emit(instr, Position.NOPOS); + } + + def emit(instr: Instruction, pos: Int) = { + assert (!closed || ignore, "BasicBlock closed"); + + if (!ignore) { +// Console.println("block " + label + ": " + instr); + instr.pos = pos; + instructionList = instr :: instructionList; + _lastInstruction = instr; + } + } + + /** Close the block */ + def close = { + assert(instructionList.length > 0, + "Empty block."); + closed = true; + instrs = toInstructionArray(instructionList.reverse); + } + + def isEmpty: Boolean = instructionList.isEmpty; + + /** Enter ignore mode: new 'emit'ted instructions will not be + * added to this basic block. It makes the generation of THROW + * and RETURNs easier. + */ + def enterIgnoreMode = ignore = true; + + def exitIgnoreMode = { + assert(ignore, "Exit ignore mode when not in ignore mode."); + ignore = false; + } + + /** Return the last instruction of this basic block. */ + def lastInstruction = + if (closed) + instrs(instrs.length - 1) + else + instructionList.head; + + def firstInstruction = + if (closed) + instrs(0) + else + instructionList.last; + + /** Convert the list to an array */ + def toInstructionArray(l: List[Instruction]): Array[Instruction] = { + var array = new Array[Instruction](l.length); + var i: Int = 0; + + l foreach (x => { array(i) = x; i = i + 1 }); + array + } + + def isClosed = closed; + + // TODO: Take care of exception handlers! + def successors : List[BasicBlock] = if (isEmpty) Nil else + lastInstruction match { + case JUMP (where) => List(where); + case CJUMP(success, failure, _, _) => failure::success::Nil; + case CZJUMP(success, failure, _, _) => failure::success::Nil; + case SWITCH(_,labels) => labels; + case RETURN(_) => Nil; + case THROW() => Nil; + case _ => + if (isClosed) + global.abort("The last instruction is not a control flow instruction: " + lastInstruction); + else Nil; + } + + /** Returns the precessors of this block, in the current 'code' chunk. + * This is signifficant only if there are exception handlers, which live + * in different code 'chunks' than the rest of the method. + */ + def predecessors: List[BasicBlock] = { + if (preds == null) + preds = code.blocks.elements.filter (p => (p.successors contains this)).toList; + preds + } + + override def equals(other: Any): Boolean = ( + other.isInstanceOf[BasicBlock] && + other.asInstanceOf[BasicBlock].label == label && + other.asInstanceOf[BasicBlock].code == code + ); + + // Instead of it, rather use a printer + def print() : unit = print(System.out); + + def print(out: java.io.PrintStream) : unit = { + out.println("block #"+label+" :"); + instructionList.reverse.foreach( + (i: Instruction) => out.println(" "+i)); + out.print("Successors: "); + successors.foreach((x: BasicBlock) => out.print(" "+x.label.toString())); + out.println(); + } + + def fullString: String = { + val buf = new StringBuffer(); + buf.append("Block ").append(label.toString()); + buf.append("\nSuccessors: ").append(successors); + buf.append("\nPredecessors: ").append(predecessors); + buf.toString() + } + + override def toString(): String = "" + label; + } + +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/CheckerError.scala b/src/compiler/scala/tools/nsc/backend/icode/CheckerError.scala new file mode 100644 index 0000000000..da1232b6e9 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/CheckerError.scala @@ -0,0 +1,11 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +class CheckerError(s: String) extends Exception(s); + diff --git a/src/compiler/scala/tools/nsc/backend/icode/Checkers.scala b/src/compiler/scala/tools/nsc/backend/icode/Checkers.scala new file mode 100644 index 0000000000..8aba76fa97 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/Checkers.scala @@ -0,0 +1,591 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.collection.mutable.{Buffer, ListBuffer, Map, HashMap}; +import scala.tools.nsc.symtab._; + +abstract class Checkers { + val global: Global; + import global._; + import global.icodes.toTypeKind; + + /** + * This class performs a set of checks similar to what the bytecode + * verifier does. For each basic block, it checks that: + * + * - for primitive operations: the type and numer of operands match + * the type of the operation + * + * - for method calls: the method exists in the type of the receiver + * and the number and type of arguments match the declared type of + * the method. + * + * - for object creation: the constructor can be called. + * + * - for load/stores: the field/local/param exists and the type + * of the value matches that of the target. + * + * For a control flow graph it checks that type stacks at entry to + * each basic block 'agree': + * + * - they have the same length + * - there exists a lub for all types at the same position in stacks. + * + * TODO: Better checks for MONITOR_ENTER/EXIT + * Better checks for local var initializations + */ + class ICodeChecker { + import icodes._; + import opcodes._; + + var clasz: IClass = _; + var method: IMethod = _; + var code: Code = _; + + val in: Map[BasicBlock, TypeStack] = new HashMap(); + val out: Map[BasicBlock, TypeStack] = new HashMap(); + + val emptyStack = new TypeStack(); + + val STRING = REFERENCE(definitions.StringClass); + val SCALA_ALL = REFERENCE(definitions.AllClass); + val SCALA_ALL_REF = REFERENCE(definitions.AllRefClass); + val CASE_CLASS = REFERENCE(definitions.getClass("scala.CaseClass")); + + def checkICodes: Unit = { + Console.println("[[consistency check at beginning of phase " + globalPhase.name + "]]"); + classes foreach check; + } + + def check(cls: IClass): Unit = { + log("Checking class " + cls); + clasz = cls; + + for (val f1 <- cls.fields; val f2 <- cls.fields; f1 ne f2) + if (f1.symbol.name == f2.symbol.name) + Checkers.this.global.error("Repetitive field name: " + + f1.symbol.fullNameString); + + for (val m1 <- cls.methods; val m2 <- cls.methods; m1 ne m2) + if (m1.symbol.name == m2.symbol.name && + m1.symbol.tpe =:= m2.symbol.tpe) + Checkers.this.global.error("Repetitive method: " + + m1.symbol.fullNameString); + clasz.methods.foreach(check); + } + + /** Apply the give funtion to each pair of the cartesian product of + * l1 x l2. + */ + def pairwise[a](l1: List[a], l2: List[a])(f: (a, a) => Unit) = + l1 foreach { x => + l2 foreach { y => f(x, y) } + } + + def check(m: IMethod): Unit = { + log("Checking method " + m); + method = m; + if (!m.isDeferred) + check(m.code); + } + + def check(c: Code): Unit = { + var worklist: Buffer[BasicBlock] = new ListBuffer(); + + def append(elems: List[BasicBlock]) = elems foreach appendBlock; + def appendBlock(bl: BasicBlock) = + if ( !worklist.exists(bl.==) ) + worklist + bl; + + in.clear; out.clear; + code = c; + worklist + c.startBlock; + c.blocks foreach ( bl => { in += bl -> emptyStack; + out += bl -> emptyStack } ); + + while (worklist.length > 0) { + val block = worklist(0); worklist.trimStart(1); + val output = check(block, in(block)); + if (output != out(block) || + (out(block) eq emptyStack)) { + log("Output changed for block: " + block.fullString); + out(block) = output; + append(block.successors); + block.successors foreach meet; + } + } + } + + /** + * Apply the meet operator of the stack lattice on bl's predecessors. + * :-). Compute the input to bl by checking that all stacks have the + * same length, and taking the lub of types at the same positions. + */ + def meet(bl: BasicBlock): Unit = { + val preds = bl.predecessors; + + def meet2(s1: TypeStack, s2: TypeStack): TypeStack = { + if (s1 eq emptyStack) s2 + else if (s2 eq emptyStack) s1 + else { + if (s1.length != s2.length) + throw new CheckerError("Incompatible stacks: " + s1 + " and " + s2 + " in " + method + " at entry to block: " + bl); + new TypeStack(List.map2(s1.types, s2.types) (lub)) + } + } + + if (preds != Nil) { + in(bl) = (preds map out.apply) reduceLeft meet2; + log("Input changed for block: " + bl +" to: " + in(bl)); + } + } + + + private var typeStack: TypeStack = null; + private var instruction: Instruction = null; + private var basicBlock: BasicBlock = null; + + /** + * Check the basic block to be type correct and return the + * produced type stack. + */ + def check(b: BasicBlock, initial: TypeStack): TypeStack = { + log("** Checking block:\n" + b.fullString + " with initial stack:\n" + initial); + var stack = new TypeStack(initial); + + this.typeStack = stack; + this.basicBlock = b; + + def typeError(k1: TypeKind, k2: TypeKind): Unit = + error(" expected: " + k1 + " but " + k2 + " found"); + + b traverse (instr => { + + def checkStack(len: Int) = + if (stack.length < len) + ICodeChecker.this.error("Expected at least " + len + " elements on the stack", stack); + else + (); + + def checkLocal(local: Local) = + method.lookupLocal(local.sym.name) match { + case None => error(" " + local + " is not defined in method " + method); + case _ => () + } + + def checkField(obj: TypeKind, field: Symbol) = + obj match { + case REFERENCE(sym) => + if (sym.info.member(field.name) == NoSymbol) + error(" " + field + " is not defined in class " + clasz); + case _ => + error(" expected reference type, but " + obj + " found"); + } + + /** Checks that tpe is a subtype of one of the allowed types */ + def checkType(tpe: TypeKind, allowed: TypeKind*) = + if (isOneOf(tpe, allowed: _*)) + () + else + error(tpe.toString() + " is not one of: " + allowed); + + /** Checks that the 2 topmost elements on stack are of the + * kind TypeKind. + */ + def checkBinop(kind: TypeKind) = { + val Pair(a, b) = stack.pop2; + checkType(a, kind); + checkType(b, kind); + } + + /** Check that arguments on the stack match method params. */ + def checkMethodArgs(method: Symbol) = { + val params = method.info.paramTypes; + checkStack(params.length); + params.reverse.foreach( (tpe) => checkType(stack.pop, toTypeKind(tpe))); + } + + /** Checks that the object passed as receiver has a method + * 'method' and that it is callable from the current method. + */ + def checkMethod(receiver: TypeKind, method: Symbol) = + receiver match { + case REFERENCE(sym) => + checkBool(sym.info.member(method.name) != NoSymbol, + "Method " + method + " does not exist in " + sym.fullNameString); + if (method hasFlag Flags.PRIVATE) + checkBool(method.owner == clasz.symbol, + "Cannot call private method of " + method.owner.fullNameString + + " from " + clasz.symbol.fullNameString); + else if (method hasFlag Flags.PROTECTED) + checkBool(clasz.symbol isSubClass method.owner, + "Cannot call protected method of " + method.owner.fullNameString + + " from " + clasz.symbol.fullNameString); + + case ARRAY(_) => + checkBool(receiver.toType.member(method.name) != NoSymbol, + "Method " + method + " does not exist in " + receiver); + + case t => + error("Not a reference type: " + t); + } + + def checkBool(cond: Boolean, msg: String) = + if (cond) () else error(msg); + + this.instruction = instr; + + if (settings.debug.value) { + log("PC: " + instr); + log("stack: " + stack); + log("================"); + } + instr match { + case THIS(clasz) => + stack push toTypeKind(clasz.tpe); + + case CONSTANT(const) => + stack push toTypeKind(const.tpe); + + case LOAD_ARRAY_ITEM(kind) => + checkStack(2); + stack.pop2 match { + case Pair(INT, ARRAY(elem)) => + if (!(elem <:< kind)) + typeError(kind, elem); + stack.push(elem); + case Pair(a, b) => + error(" expected and INT and a array reference, but " + + a + ", " + b + " found"); + } + + case LOAD_LOCAL(local, isArg) => + checkLocal(local); + stack.push(local.kind); + + case LOAD_FIELD(field, isStatic) => + if (isStatic) { + // the symbol's owner should contain it's field, but + // this is already checked by the type checker, no need + // to redo that here + } else { + checkStack(1); + val obj = stack.pop; + checkField(obj, field); + } + stack.push(toTypeKind(field.tpe)); + + case LOAD_MODULE(module) => + checkBool((module.isModule || module.isModuleClass), + "Expected module: " + module + " flags: " + Flags.flagsToString(module.flags)); + stack.push(toTypeKind(module.tpe)); + + case STORE_ARRAY_ITEM(kind) => + checkStack(3); + stack.pop3 match { + case Triple(k, INT, ARRAY(elem)) => + if (!(k <:< kind)) + typeError(kind, k); + if (!(k <:< elem)) + typeError(elem, k); + case Triple(a, b, c) => + error(" expected and array reference, and int and " + kind + + " but " + a + ", " + b + ", " + c + " found"); + } + + case STORE_LOCAL(local, isArg) => + checkLocal(local); + checkStack(1); + + val actualType = stack.pop; + if (!(actualType <:< local.kind) && + actualType != CASE_CLASS && + local.kind != SCALA_ALL_REF) + typeError(local.kind, actualType); + + case STORE_FIELD(field, isStatic) => + if (isStatic) { + checkStack(1); + val fieldType = toTypeKind(field.tpe); + val actualType = stack.pop; + if (!(actualType <:< fieldType)) + typeError(fieldType, actualType); + } else { + checkStack(2); + stack.pop2 match { + case Pair(value, obj) => + checkField(obj, field); + val fieldType = toTypeKind(field.tpe); + if (fieldType != SCALA_ALL_REF && !(value <:< fieldType)) + typeError(fieldType, value); + } + } + + case CALL_PRIMITIVE(primitive) => + checkStack(instr.consumed); + primitive match { + case Negation(kind) => + checkType(kind, BOOL, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE); + checkType(stack.pop, kind); + stack push kind; + + case Test(op, kind, zero) => + if (zero) { + val actualType = stack.pop; + checkType(actualType, kind); + } else + checkBinop(kind); + stack push BOOL; + + case Comparison(op, kind) => + checkType(kind, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE); + checkBinop(kind); + stack push INT; + + case Arithmetic(op, kind) => + checkType(kind, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE); + if (op == NOT) + checkType(stack.pop, kind) + else + checkBinop(kind); + stack push kind; + + case Logical(op, kind) => + checkType(kind, BOOL, BYTE, CHAR, SHORT, INT, LONG); + checkBinop(kind); + stack push kind; + + case Shift(op, kind) => + checkType(kind, BYTE, CHAR, SHORT, INT, LONG); + val Pair(a, b) = stack.pop2; + checkType(a, INT); + checkType(b, kind); + stack push kind; + + case Conversion(src, dst) => + checkType(src, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE); + checkType(dst, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE); + checkType(stack.pop, src); + stack push dst; + + case ArrayLength(kind) => + val arr = stack.pop; + arr match { + case ARRAY(elem) => + checkType(elem, kind); + case _ => + error(" array reference expected, but " + arr + " found"); + } + stack push INT; + + case StartConcat => + stack.push(ConcatClass); + + case EndConcat => + checkType(stack.pop, ConcatClass); + stack.push(STRING); + + case StringConcat(el) => + checkType(stack.pop, el); + checkType(stack.pop, ConcatClass); + stack push ConcatClass; + } + + case CALL_METHOD(method, style) => + style match { + case Dynamic => + checkStack(1 + method.info.paramTypes.length); + checkMethodArgs(method); + checkMethod(stack.pop, method); + stack.push(toTypeKind(method.info.resultType)); + + case Static(onInstance) => + if (onInstance) { + checkStack(1 + method.info.paramTypes.length); + checkBool(method.hasFlag(Flags.PRIVATE) || method.isConstructor, + "Static call to non-private method."); + checkMethodArgs(method); + checkMethod(stack.pop, method); + if (!method.isConstructor) + stack.push(toTypeKind(method.info.resultType)); + } else { + checkStack(method.info.paramTypes.length); + checkMethodArgs(method); + stack.push(toTypeKind(method.info.resultType)); + } + + case SuperCall(mixin) => + checkStack(1 + method.info.paramTypes.length); + checkMethodArgs(method); + checkMethod(stack.pop, method); + stack.push(toTypeKind(method.info.resultType)); + + } + + case NEW(kind) => + kind match { + case REFERENCE(cls) => + stack.push(kind); + + case _ => error("NEW call to non-reference type: " + kind); + } + + case CREATE_ARRAY(elem) => + checkStack(1); + checkType(stack.pop, INT); + stack.push(ARRAY(elem)); + + case IS_INSTANCE(tpe) => + val ref = stack.pop; + checkBool(ref.isReferenceType || ref.isArrayType, + "IS_INSTANCE on primitive type: " + ref); + checkBool(tpe.isReferenceType || tpe.isArrayType, + "IS_INSTANCE to primitive type: " + tpe); + stack.push(BOOL); + + case CHECK_CAST(tpe) => + val ref = stack.pop; + checkBool(ref.isReferenceType || ref.isArrayType, + "CHECK_CAST on primitive type: " + ref); + checkBool(tpe.isReferenceType || tpe.isArrayType, + "CHECK_CAST to primitive type: " + tpe); + stack.push(tpe); + + case SWITCH(tags, labels) => + checkType(stack.pop, INT); + checkBool(tags.length == labels.length - 1, + "The number of tags and labels does not coincide."); + checkBool(labels forall (b => code.blocks contains b), + "Switch target cannot be found in code."); + + case JUMP(where) => + checkBool(code.blocks contains where, + "Jump to non-existant block " + where); + + case CJUMP(success, failure, cond, kind) => + checkBool(code.blocks contains success, + "Jump to non-existant block " + success); + checkBool(code.blocks contains failure, + "Jump to non-existant block " + failure); + checkBinop(kind); + + case CZJUMP(success, failure, cond, kind) => + checkBool(code.blocks contains success, + "Jump to non-existant block " + success); + checkBool(code.blocks contains failure, + "Jump to non-existant block " + failure); + checkType(stack.pop, kind); + + case RETURN(kind) => + kind match { + case UNIT => (); + + case REFERENCE(_) | ARRAY(_) => + checkStack(1); + val top = stack.pop; + checkBool(top.isReferenceType || top.isArrayType, + "" + kind + " is a reference type, but " + top + " is not"); + case _ => + checkStack(1); + val top = stack.pop; + checkType(top, kind); + } + + case THROW() => + val thrown = stack.pop; + checkBool(thrown.toType <:< definitions.ThrowableClass.tpe, + "Element on top of stack should implement 'Throwable': " + thrown); + stack.push(SCALA_ALL); + + case DROP(kind) => + checkType(stack.pop, kind); + + case DUP(kind) => + val top = stack.pop; + checkType(top, kind); + stack.push(top); + stack.push(top); + + case MONITOR_ENTER() => + checkStack(1); + checkBool(stack.pop.isReferenceType, + "MONITOR_ENTER on non-reference type"); + + case MONITOR_EXIT() => + checkStack(1); + checkBool(stack.pop.isReferenceType, + "MONITOR_EXIT on non-reference type"); + + case _ => abort("Unknown instruction: " + instr); + } + }); + stack + } + + //////////////// Error reporting ///////////////////////// + + def error(msg: String): Unit = { + System.out.println(method.toString() + " in block: " + basicBlock.label); + printLastIntructions; + + Checkers.this.global.error("ICode checker: " + method + ": " + msg); + } + + /** Prints the last 4 instructions. */ + def printLastIntructions = { + var printed = 0; + var buf: List[Instruction] = Nil; + + basicBlock.traverseBackwards( (i) => + if (i == instruction || (printed > 0 && printed < 3)) { + buf = i :: buf; + printed = printed + 1; + }); + buf foreach System.out.println; + Console.println("at: " + clasz.cunit.position(buf.head.pos)); + } + + def error(msg: String, stack: TypeStack): Unit = { + error(msg + "\n type stack: " + stack); + } + + + //////////////////// Checking ///////////////////////////// + + /** Return true if k1 is a subtype of any of the following types. */ + def isOneOf(k1: TypeKind, kinds: TypeKind*) = + kinds.exists( k => k1 <:< k); + + + /** + * Dummy TypeKind to represent the ConcatClass in a platform-independent + * way. For JVM it would have been a REFERENCE to 'StringBuffer'. + */ + case object ConcatClass extends TypeKind { + override def toString() = "ConcatClass"; + + /** + * Approximate `lub'. The common type of two references is + * always AnyRef. For 'real' least upper bound wrt to subclassing + * use method 'lub'. + */ + override def maxType(other: TypeKind): TypeKind = + other match { + case REFERENCE(_) => REFERENCE(definitions.AnyRefClass); + case _ => + abort("Uncomparbale type kinds: ConcatClass with " + other); + } + + /** Checks subtyping relationship. */ + override def <:<(other: TypeKind): Boolean = (this eq other); + + override def isReferenceType: Boolean = false; + } + } +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala new file mode 100644 index 0000000000..126e20ebef --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala @@ -0,0 +1,49 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.collection.mutable.HashMap; +import scala.collection.mutable.HashSet; + +/** + * Exception handlers are pieces of code that `handle' exceptions on + * the covered basic blocks. Since Scala's exception handling uses + * pattern matching instead of just class names to identify handlers, + * all our handlers will catch `Throwable' and rely on proper ordering + * in the generated code to preserve nesting. + */ +trait ExceptionHandlers: ICodes { + import global.{Symbol, NoSymbol}; + + class ExceptionHandler(val method: IMethod, label: String, val cls: Symbol) { + private var coveredBlocks: List[BasicBlock] = Nil; + private var _startBlock: BasicBlock = _; + var finalizer: Finalizer = _; + + def setStartBlock(b: BasicBlock) = _startBlock = b; + def startBlock = _startBlock; + + def addBlock(b: BasicBlock): ExceptionHandler = { + coveredBlocks = b :: coveredBlocks; + this + } + + def covered: List[BasicBlock] = coveredBlocks; + + override def toString() = "exh_" + label + "(" + cls.simpleName + ")"; + } + + class Finalizer(method: IMethod, label: String) extends ExceptionHandler(method, label, NoSymbol) { + override def toString() = "finalizer_" + label; + } + + object NoFinalizer extends Finalizer(null, "<no finalizer>") { + override def startBlock: BasicBlock = error("NoFinalizer cannot have a start block."); + override def setStartBlock(b: BasicBlock): Unit = error("NoFinalizer cannot have a start block."); + } +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala new file mode 100644 index 0000000000..a7940cd66a --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -0,0 +1,1672 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.collection.mutable.{Map, HashMap}; +import scala.tools.nsc.symtab._; + + +/** + * TODO: + * - switches with alternatives + */ +abstract class GenICode extends SubComponent { + import global._; + import icodes._; + import icodes.opcodes._; + + val phaseName = "icode"; + + override def newPhase(prev: Phase) = new ICodePhase(prev); + + class ICodePhase(prev: Phase) extends StdPhase(prev) { + override def name = "icode"; + + override def description = "Generate ICode from the AST"; + + var unit: CompilationUnit = _; + + // We assume definitions are alread initialized + val STRING = REFERENCE(definitions.StringClass); + + // this depends on the backend! should be changed. + val ANY_REF_CLASS = REFERENCE(definitions.ObjectClass); + + val SCALA_ALL = REFERENCE(definitions.AllClass); + val SCALA_ALLREF = REFERENCE(definitions.AllRefClass); + val THROWABLE = REFERENCE(definitions.ThrowableClass); + + /////////////////////////////////////////////////////////// + + override def run: Unit = { + scalaPrimitives.init; + super.run + } + + override def apply(unit: CompilationUnit): Unit = { + this.unit = unit; + log("Generating icode for " + unit); + gen(unit.body); + } + + def gen(tree: Tree): Context = gen(tree, new Context()); + + def gen(trees: List[Tree], ctx: Context): Context = { + var ctx1 = ctx; + for (val t <- trees) + ctx1 = gen(t, ctx1); + + ctx1 + } + + /////////////////// Code generation /////////////////////// + + def gen(tree: Tree, ctx: Context): Context = tree match { + case EmptyTree => ctx; + + case PackageDef(name, stats) => gen(stats, ctx setPackage name); + + case ClassDef(mods, name, tparams, tpt, impl) => + log("Generating class: " + tree.symbol.fullNameString); + ctx setClass (new IClass(tree.symbol) setCompilationUnit unit); + addClassFields(ctx, tree.symbol); + classes = ctx.clazz :: classes; + gen(impl, ctx); + ctx setClass null; + + // !! modules should be eliminated by refcheck... or not? + case ModuleDef(mods, name, impl) => + abort("Modules should not reach backend!"); + + log("Generating module: " + tree.symbol.fullNameString); + ctx setClass (new IClass(tree.symbol) setCompilationUnit unit); + addClassFields(ctx, tree.symbol); + classes = ctx.clazz :: classes; + gen(impl, ctx); + ctx setClass null; + + case ValDef(mods, name, tpt, rhs) => ctx; // we use the symbol to add fields + + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + if (settings.debug.value) + log("Entering method " + name); + val m = new IMethod(tree.symbol); + m.sourceFile = unit.source.toString(); + m.returnType = if (tree.symbol.isConstructor) UNIT + else toTypeKind(tree.symbol.info.resultType); + ctx.clazz.addMethod(m); + + var ctx1 = ctx.enterMethod(m, tree.asInstanceOf[DefDef]); + addMethodParams(ctx1, vparamss); + + if (!m.isDeferred) { + ctx1 = genLoad(rhs, ctx1, m.returnType); + + // reverse the order of the local variables, to match the source-order + m.locals = m.locals.reverse; + + rhs match { + case Block(_, Return(_)) => (); + case Return(_) => (); + case _ => if (ctx1.bb.isEmpty) + ctx1.bb.emit(RETURN(m.returnType), rhs.pos); + else + ctx1.bb.emit(RETURN(m.returnType)); + } + ctx1.bb.close; + prune(ctx1.method); + } else + ctx1.method.setCode(null); + ctx1; + + case Template(parents, body) => + gen(body, ctx); + + case _ => + abort("Illegal tree in gen: " + tree); + } + + private def genStat(trees: List[Tree], ctx: Context): Context = { + var currentCtx = ctx; + + for (val t <- trees) + currentCtx = genStat(t, currentCtx); + + currentCtx + } + + /** + * Generate code for the given tree. The trees should contain statements + * and not produce any value. Use genLoad for expressions which leave + * a value on top of the stack. + * + * @return a new context. This is necessary for control flow instructions + * which may change the current basic block. + */ + private def genStat(tree: Tree, ctx: Context): Context = tree match { + case Assign(lhs @ Select(_, _), rhs) => + if (isStaticSymbol(lhs.symbol)) { + val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info)); + ctx1.bb.emit(STORE_FIELD(lhs.symbol, true), tree.pos); + ctx1 + } else { + var ctx1 = genLoadQualifier(lhs, ctx); + ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info)); + ctx1.bb.emit(STORE_FIELD(lhs.symbol, false), tree.pos); + ctx1 + } + + case Assign(lhs, rhs) => +// assert(ctx.method.locals.contains(lhs.symbol) | ctx.clazz.fields.contains(lhs.symbol), +// "Assignment to inexistent local or field: " + lhs.symbol); + val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info)); + val Some(l) = ctx.method.lookupLocal(lhs.symbol); + ctx1.bb.emit(STORE_LOCAL(l, lhs.symbol.isValueParameter), tree.pos); + ctx1 + + case _ => + if (settings.debug.value) + log("Passing " + tree + " to genLoad"); + genLoad(tree, ctx, UNIT); + } + + /** + * Generate code for trees that produce values on the stack + * + * @param tree The tree to be translated + * @param ctx The current context + * @param expectedType The type of the value to be generated on top of the + * stack. + * @return The new context. The only thing that may change is the current + * basic block (as the labels map is mutable). + */ + private def genLoad(tree: Tree, ctx: Context, expectedType: TypeKind): Context = { + var generatedType = expectedType; + + /** + * Generate code for primitive arithmetic operations. + */ + def genArithmeticOp(tree: Tree, ctx: Context, code: Int): Context = { + val Apply(fun @ Select(larg, _), args) = tree; + var ctx1 = ctx; + var resKind = toTypeKind(larg.tpe); + + assert(args.length <= 1, + "Too many arguments for primitive function: " + fun.symbol); + assert(resKind.isNumericType | resKind == BOOL, + resKind.toString() + " is not a numeric or boolean type [operation: " + fun.symbol + "]"); + + args match { + // unary operation + case Nil => + ctx1 = genLoad(larg, ctx1, resKind); + code match { + case scalaPrimitives.POS => (); // nothing + case scalaPrimitives.NEG => ctx1.bb.emit(CALL_PRIMITIVE(Negation(resKind)), larg.pos); + case scalaPrimitives.NOT => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(NOT, resKind)), larg.pos); + case _ => abort("Unknown unary operation: " + fun.symbol.fullNameString + " code: " + code); + } + generatedType = resKind; + + // binary operation + case rarg :: Nil => + resKind = getMaxType(larg.tpe :: rarg.tpe :: Nil); + if (scalaPrimitives.isShiftOp(code) || scalaPrimitives.isBitwiseOp(code)) + assert(resKind.isIntType | resKind == BOOL, + resKind.toString() + " incompatible with arithmetic modulo operation: " + ctx1); + + ctx1 = genLoad(larg, ctx1, resKind); + ctx1 = genLoad(rarg, + ctx1, // check .NET size of shift arguments! + if (scalaPrimitives.isShiftOp(code)) INT else resKind); + + generatedType = resKind; + code match { + case scalaPrimitives.ADD => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(ADD, resKind)), tree.pos); + case scalaPrimitives.SUB => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(SUB, resKind)), tree.pos); + case scalaPrimitives.MUL => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(MUL, resKind)), tree.pos); + case scalaPrimitives.DIV => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(DIV, resKind)), tree.pos); + case scalaPrimitives.MOD => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(REM, resKind)), tree.pos); + case scalaPrimitives.OR => ctx1.bb.emit(CALL_PRIMITIVE(Logical(OR, resKind)), tree.pos); + case scalaPrimitives.XOR => ctx1.bb.emit(CALL_PRIMITIVE(Logical(XOR, resKind)), tree.pos); + case scalaPrimitives.AND => ctx1.bb.emit(CALL_PRIMITIVE(Logical(AND, resKind)), tree.pos); + case scalaPrimitives.LSL => ctx1.bb.emit(CALL_PRIMITIVE(Shift(LSL, resKind)), tree.pos); + generatedType = resKind; + case scalaPrimitives.LSR => ctx1.bb.emit(CALL_PRIMITIVE(Shift(LSR, resKind)), tree.pos); + generatedType = resKind; + case scalaPrimitives.ASR => ctx1.bb.emit(CALL_PRIMITIVE(Shift(ASR, resKind)), tree.pos); + generatedType = resKind; + case _ => abort("Unknown primitive: " + fun.symbol + "[" + code + "]"); + } + + case _ => abort("Too many arguments for primitive function: " + tree); + } + ctx1 + } + + /** Generate primitive array operations. */ + def genArrayOp(tree: Tree, ctx: Context, code: Int): Context = { + import scalaPrimitives._; + val Apply(Select(arrayObj, _), args) = tree; + val k = toTypeKind(arrayObj.tpe); + val ARRAY(elem) = k; + var ctx1 = genLoad(arrayObj, ctx, k); + + if (scalaPrimitives.isArrayGet(code)) { + // load argument on stack + assert(args.length == 1, + "Too many arguments for array get operation: " + tree); + ctx1 = genLoad(args.head, ctx1, INT); + generatedType = elem; + } else if (scalaPrimitives.isArraySet(code)) { + assert(args.length == 2, + "Too many arguments for array set operation: " + tree); + ctx1 = genLoad(args.head, ctx1, INT); + ctx1 = genLoad(args.tail.head, ctx1, toTypeKind(args.tail.head.tpe)); + // the following line should really be here, but because of bugs in erasure + // we pretend we generate whatever type is expected from us. + //generatedType = UNIT; + } else + generatedType = INT; + + code match { + case ZARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(BOOL)), tree.pos); + case BARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(BYTE)), tree.pos); + case SARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(SHORT)), tree.pos); + case CARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(CHAR)), tree.pos); + case IARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(INT)), tree.pos); + case LARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(LONG)), tree.pos); + case FARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(FLOAT)), tree.pos); + case DARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(DOUBLE)), tree.pos); + case OARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(ANY_REF_CLASS)), tree.pos); + + case ZARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(BOOL), tree.pos); + case BARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(BYTE), tree.pos); + case SARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(SHORT), tree.pos); + case CARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(CHAR), tree.pos); + case IARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(INT), tree.pos); + case LARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(LONG), tree.pos); + case FARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(FLOAT), tree.pos); + case DARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(DOUBLE), tree.pos); + case OARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(ANY_REF_CLASS), tree.pos); + + case ZARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(BOOL), tree.pos); + case BARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(BYTE), tree.pos); + case SARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(SHORT), tree.pos); + case CARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(CHAR), tree.pos); + case IARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(INT), tree.pos); + case LARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(LONG), tree.pos); + case FARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(FLOAT), tree.pos); + case DARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(DOUBLE), tree.pos); + case OARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(ANY_REF_CLASS), tree.pos); + + case _ => + abort("Unknown operation on arrays: " + tree + " code: " + code); + } + ctx1 + } + + // genLoad + val resCtx: Context = tree match { + case LabelDef(name, params, rhs) => + val ctx1 = ctx.newBlock; + ctx1.labels.get(tree.symbol) match { + case Some(label) => + label.anchor(ctx1.bb); + label.patch(ctx.method.code); + + case None => + ctx1.labels += tree.symbol -> (new Label(tree.symbol) anchor ctx1.bb setParams (params map (.symbol))); + ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info)))); + if (settings.debug.value) + log("Adding label " + tree.symbol); + } + +// if (!isTailCallLabel(tree.asInstanceOf[LabelDef], ctx)) { +// log("Non-tail call label found (" + tree.symbol + "), initializing arguments to default values."); +// genLoadLabelArguments(params map { p => zeroOf(toTypeKind(p.symbol.tpe)) }, +// ctx1.labels(tree.symbol), +// ctx); +// } + + ctx.bb.emit(JUMP(ctx1.bb), tree.pos); + ctx.bb.close; + genLoad(rhs, ctx1, expectedType /*toTypeKind(tree.symbol.info.resultType)*/); + + case ValDef(_, _, _, rhs) => + val sym = tree.symbol; + val local = new Local(sym, toTypeKind(sym.info)); + ctx.method.addLocal(local); + + if (rhs == EmptyTree) { + if (settings.debug.value) + log("Uninitialized variable " + tree + " at: " + unit.position(tree.pos)); + ctx.bb.emit(getZeroOf(local.kind)); + } + + var ctx1 = ctx; + if (rhs != EmptyTree) + ctx1 = genLoad(rhs, ctx, local.kind); + + ctx1.bb.emit(STORE_LOCAL(local, false), tree.pos); + generatedType = UNIT; + ctx1 + + case If(cond, thenp, elsep) => + var thenCtx = ctx.newBlock; + var elseCtx = ctx.newBlock; + val contCtx = ctx.newBlock; + genCond(cond, ctx, thenCtx, elseCtx); + val ifKind = toTypeKind(tree.tpe); + + val thenKind = toTypeKind(thenp.tpe); + val elseKind = if (elsep == EmptyTree) UNIT else toTypeKind(elsep.tpe); + + generatedType = ifKind; + + // we need to drop unneeded results, if one branch gives + // unit and the other gives something on the stack, because + // the type of 'if' is scala.Any, and its erasure would be Object. + // But unboxed units are not Objects... + if (thenKind == UNIT || elseKind == UNIT) { + if (settings.debug.value) + log("Will drop result from an if branch"); + thenCtx = genLoad(thenp, thenCtx, UNIT); + elseCtx = genLoad(elsep, elseCtx, UNIT); + assert(expectedType == UNIT, "I produce UNIT in a context where " + expectedType + " is expected!"); + generatedType = UNIT; + } else { + thenCtx = genLoad(thenp, thenCtx, ifKind); + elseCtx = genLoad(elsep, elseCtx, ifKind); + } + + thenCtx.bb.emit(JUMP(contCtx.bb)); + thenCtx.bb.close; + elseCtx.bb.emit(JUMP(contCtx.bb)); + elseCtx.bb.close; + + contCtx; + + case Return(expr) => + val returnedKind = toTypeKind(expr.tpe); + val ctx1 = genLoad(expr, ctx, returnedKind); + ctx1.bb.emit(RETURN(returnedKind), tree.pos); + ctx1.bb.enterIgnoreMode; + generatedType = expectedType; + ctx1 + + case Try(block, catches, finalizer) => + val kind = toTypeKind(tree.tpe); + var handlers = for (val CaseDef(pat, _, body) <- catches.reverse) + yield pat match { + case Typed(Ident(nme.WILDCARD), tpt) => Pair(tpt.tpe.symbol, { + ctx: Context => + ctx.bb.emit(DROP(REFERENCE(tpt.tpe.symbol))); + val ctx1 = genLoad(finalizer, ctx, UNIT); + genLoad(body, ctx1, kind); + }) + + case Ident(nme.WILDCARD) => Pair(definitions.ThrowableClass, { + ctx: Context => + ctx.bb.emit(DROP(REFERENCE(definitions.ThrowableClass))); + val ctx1 = genLoad(finalizer, ctx, UNIT); + genLoad(body, ctx1, kind) + }) + + case Bind(name, _) => + val exception = new Local(pat.symbol, toTypeKind(pat.symbol.tpe)); + ctx.method.addLocal(exception); + + Pair(pat.symbol.tpe.symbol, { + ctx: Context => + ctx.bb.emit(STORE_LOCAL(exception, false), pat.pos); + val ctx1 = genLoad(finalizer, ctx, UNIT); + genLoad(body, ctx1, kind) + }) + } + + if (finalizer != EmptyTree) + handlers = Pair(NoSymbol, { + ctx: Context => + val ctx1 = genLoad(finalizer, ctx, UNIT); + ctx1.bb.emit(THROW()); + ctx1 + }) :: handlers; + + ctx.Try( + bodyCtx => { + generatedType = toTypeKind(block.tpe); + val ctx1 = genLoad(block, bodyCtx, generatedType); + genLoad(finalizer, ctx1, UNIT) + }, + handlers) + + case Throw(expr) => + val ctx1 = genLoad(expr, ctx, THROWABLE); + ctx1.bb.emit(THROW(), tree.pos); + ctx1.bb.enterIgnoreMode; + generatedType = SCALA_ALL; + ctx1; + + case New(tpt) => + abort("Unexpected New"); + + case Apply(TypeApply(fun, targs), _) => + val sym = fun.symbol; + var ctx1 = ctx; + var cast = false; + + if (sym == definitions.Object_isInstanceOf) + cast = false + else if (sym == definitions.Object_asInstanceOf) + cast = true + else + abort("Unexpected type application " + fun + "[sym: " + sym + "]"); + + val Select(obj, _) = fun; + val l = toTypeKind(obj.tpe); + val r = toTypeKind(targs.head.tpe); + + ctx1 = genLoadQualifier(fun, ctx); + + if (l.isValueType && r.isValueType) + genConversion(l, r, ctx1, cast) + else if (l.isValueType) { + ctx1.bb.emit(DROP(l), fun.pos); + if (cast) { + ctx1.bb.emit(NEW(REFERENCE(definitions.getClass("ClassCastException")))); + ctx1.bb.emit(DUP(ANY_REF_CLASS)); + ctx1.bb.emit(THROW()); + } else + ctx1.bb.emit(CONSTANT(Constant(false))) + } + else if (r.isValueType /*|| r.isArrayType */) + genBoxedConversion(l, r, ctx1, cast) + else + genCast(l, r, ctx1, cast); + + generatedType = if (cast) r else BOOL; + ctx1 + + // 'super' call: Note: since constructors are supposed to + // return an instance of what they construct, we have to take + // special care. On JVM they are 'void', and Scala forbids (syntactically) + // to call super constructors explicitly and/or use their 'returned' value. + // therefore, we can ignore this fact, and generate code that leaves nothing + // on the stack (contrary to what the type in the AST says). + case Apply(fun @ Select(Super(_, mixin), _), args) => + if (settings.debug.value) + log("Call to super: " + tree); + val invokeStyle = SuperCall(mixin); +// if (fun.symbol.isConstructor) Static(true) else SuperCall(mixin); + + ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos); + val ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx); + + ctx1.bb.emit(CALL_METHOD(fun.symbol, invokeStyle), tree.pos); + generatedType = if (fun.symbol.isConstructor) + UNIT + else + toTypeKind(fun.symbol.info.resultType); + ctx1 + + // 'new' constructor call: Note: since constructors are + // thought to return an instance of what they construct, + // we have to 'simulate' it by DUPlicating the freshly created + // instance (on JVM, <init> methods return VOID). + case Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) => + val ctor = fun.symbol; + assert(ctor.isClassConstructor, + "'new' call to non-constructor: " + tree); + + generatedType = toTypeKind(tpt.tpe); + assert(generatedType.isReferenceType || generatedType.isArrayType, + "Non reference type cannot be instantiated: " + generatedType); + + var ctx1 = ctx; + + generatedType match { + case ARRAY(elem) => + ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx); + ctx1.bb.emit(CREATE_ARRAY(elem), tree.pos); + + case REFERENCE(cls) => + assert(ctor.owner == cls, + "Symbol " + ctor.owner.fullNameString + "is different than " + tpt); + ctx1.bb.emit(NEW(generatedType), tree.pos); + ctx1.bb.emit(DUP(generatedType)); + ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx); + + ctx1.bb.emit(CALL_METHOD(ctor, Static(true)), tree.pos); + + case _ => + abort("Cannot instantiate " + tpt + "of kind: " + generatedType); + } + ctx1 + + case Apply(fun, args) => + val sym = fun.symbol; + + if (sym.isLabel) { // jump to a label + val label = ctx.labels.get(sym) match { + case Some(l) => l; + + // it is a forward jump, scan for labels + case None => + log("Performing scan for label because of forward jump."); + scanForLabels(ctx.defdef, ctx); + ctx.labels.get(sym) match { + case Some(l) => + log("Found label: " + l); + l + case _ => abort("Unknown label target: " + sym + " at: " + unit.position(fun.pos) + ": ctx: " + ctx); + } + } + val ctx1 = genLoadLabelArguments(args, label, ctx); + if (label.anchored) + ctx1.bb.emit(JUMP(label.block), tree.pos); + else + ctx1.bb.emit(PJUMP(label), tree.pos); + + ctx1.bb.close; + ctx1.newBlock; + } else if (isPrimitive(fun.symbol)) { // primitive method call + val Select(receiver, _) = fun; + + val code = scalaPrimitives.getPrimitive(fun.symbol, receiver.tpe); + var ctx1 = ctx; + + if (scalaPrimitives.isArithmeticOp(code)) { + ctx1 = genArithmeticOp(tree, ctx1, code); + } else if (code == scalaPrimitives.CONCAT) { + ctx1 = genStringConcat(tree, ctx1); + generatedType = STRING; + } else if (scalaPrimitives.isArrayOp(code)) { + ctx1 = genArrayOp(tree, ctx1, code); + } else if (scalaPrimitives.isLogicalOp(code) || + scalaPrimitives.isComparisonOp(code)) { + + val trueCtx = ctx1.newBlock; + val falseCtx = ctx1.newBlock; + val afterCtx = ctx1.newBlock; + if (settings.debug.value) + log("Passing " + tree + " to genCond"); + genCond(tree, ctx1, trueCtx, falseCtx); + trueCtx.bb.emit(CONSTANT(Constant(true)), tree.pos); + trueCtx.bb.emit(JUMP(afterCtx.bb)); + trueCtx.bb.close; + falseCtx.bb.emit(CONSTANT(Constant(false)), tree.pos); + falseCtx.bb.emit(JUMP(afterCtx.bb)); + falseCtx.bb.close; + generatedType = BOOL; + ctx1 = afterCtx; + } else if (code == scalaPrimitives.SYNCHRONIZED) { + val monitor = new Local(ctx.method.symbol.newVariable(tree.pos, unit.fresh.newName("monitor")).setInfo(definitions.ObjectClass.tpe), + ANY_REF_CLASS); + ctx.method.addLocal(monitor); + + ctx1 = genLoadQualifier(fun, ctx1); + ctx1.bb.emit(DUP(ANY_REF_CLASS)); + ctx1.bb.emit(STORE_LOCAL(monitor, false)); + ctx1.bb.emit(MONITOR_ENTER(), tree.pos); + + if (settings.debug.value) + log("synchronized block start"); + ctx1 = ctx1.Try( + bodyCtx => { + val ctx1 = genLoad(args.head, bodyCtx, toTypeKind(tree.tpe.resultType)); + ctx1.bb.emit(LOAD_LOCAL(monitor, false)); + ctx1.bb.emit(MONITOR_EXIT(), tree.pos); + ctx1 + }, List( + Pair(NoSymbol, exhCtx => { + exhCtx.bb.emit(LOAD_LOCAL(monitor, false)); + exhCtx.bb.emit(MONITOR_EXIT(), tree.pos); + exhCtx.bb.emit(THROW()); + exhCtx + }))); + if (settings.debug.value) + log("synchronized block end with block " + ctx1.bb + " closed=" + ctx1.bb.isClosed); + } else if (scalaPrimitives.isCoercion(code)) { + ctx1 = genLoad(receiver, ctx1, toTypeKind(receiver.tpe)); + genCoercion(tree, ctx1, code); + } else + abort("Primitive operation not handled yet: " + + fun.symbol.fullNameString + "(" + fun.symbol.simpleName + ") " + + " at: " + unit.position(tree.pos)); + ctx1 + } else { // normal method call + if (settings.debug.value) + log("Gen CALL_METHOD with sym: " + sym + " isStaticSymbol: " + isStaticSymbol(sym)); + var invokeStyle = + if (isStaticSymbol(sym)) + Static(false) + else if (sym.hasFlag(Flags.PRIVATE) || sym.isClassConstructor) + Static(true) + else + Dynamic; + + var ctx1 = if (invokeStyle.hasInstance) + genLoadQualifier(fun, ctx); + else + ctx; + + ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx1); + + ctx1.bb.emit(CALL_METHOD(sym, invokeStyle), tree.pos); + generatedType = if (sym.isClassConstructor) UNIT else toTypeKind(sym.info.resultType); + ctx1 + } + + case This(qual) => + assert(tree.symbol == ctx.clazz.symbol || tree.symbol.isModuleClass, + "Trying to access the this of another class: " + + "tree.symbol = " + tree.symbol + ", ctx.clazz.symbol = " + ctx.clazz.symbol); + if (tree.symbol.isModuleClass && tree.symbol != ctx.clazz.symbol) { + if (settings.debug.value) + log("LOAD_MODULE from 'This': " + tree.symbol); + ctx.bb.emit(LOAD_MODULE(tree.symbol), tree.pos); + generatedType = REFERENCE(tree.symbol); + } else { + ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos); + generatedType = REFERENCE(ctx.clazz.symbol); + } + ctx; + + case Select(Ident(nme.EMPTY_PACKAGE_NAME), module) => + assert(tree.symbol.isModule, + "Selection of non-module from empty package: " + tree.toString() + + " sym: " + tree.symbol + + " at: " + unit.position(tree.pos)); + if (settings.debug.value) + log("LOAD_MODULE from Select(<emptypackage>): " + tree.symbol); + ctx.bb.emit(LOAD_MODULE(tree.symbol), tree.pos); + ctx + + case Select(qualifier, selector) => + val sym = tree.symbol; + generatedType = toTypeKind(sym.info); + + if (sym.isModule) { + if (settings.debug.value) + log("LOAD_MODULE from Select(qualifier, selector): " + sym); + ctx.bb.emit(LOAD_MODULE(sym), tree.pos); + ctx + } else if (isStaticSymbol(sym)) { + ctx.bb.emit(LOAD_FIELD(sym, true), tree.pos); + ctx + } else { + val ctx1 = genLoadQualifier(tree, ctx); + ctx1.bb.emit(LOAD_FIELD(sym, false), tree.pos); + ctx1 + } + + case Ident(name) => + if (!tree.symbol.isPackage) { + if (tree.symbol.isModule) { + if (settings.debug.value) + log("LOAD_MODULE from Ident(name): " + tree.symbol); + ctx.bb.emit(LOAD_MODULE(tree.symbol), tree.pos); + generatedType = toTypeKind(tree.symbol.info); + } else { + val Some(l) = ctx.method.lookupLocal(tree.symbol); + ctx.bb.emit(LOAD_LOCAL(l, tree.symbol.isValueParameter), tree.pos); + generatedType = l.kind; + } + } + ctx + + case Literal(value) => + if (value.tag != UnitTag) + ctx.bb.emit(CONSTANT(value), tree.pos); + generatedType = toTypeKind(value.tpe); + ctx + + case Block(stats, expr) => + assert(!(ctx.method eq null), "Block outside method"); + val ctx1 = genStat(stats, ctx); + genLoad(expr, ctx1, expectedType); + + case Typed(expr, _) => + genLoad(expr, ctx, expectedType); + + case Assign(_, _) => + generatedType = UNIT; + genStat(tree, ctx); + + case ArrayValue(tpt @ TypeTree(), elems) => + var ctx1 = ctx; + val elmKind = toTypeKind(tpt.tpe); + generatedType = ARRAY(elmKind); + + ctx1.bb.emit(CONSTANT(new Constant(elems.length)), tree.pos); + ctx1.bb.emit(CREATE_ARRAY(elmKind)); + // inline array literals + var i = 0; + while (i < elems.length) { + ctx1.bb.emit(DUP(generatedType), tree.pos); + ctx1.bb.emit(CONSTANT(new Constant(i))); + ctx1 = genLoad(elems(i), ctx1, elmKind); + ctx1.bb.emit(STORE_ARRAY_ITEM(elmKind)); + i = i + 1; + } + ctx1 + + case Match(selector, cases) => + if (settings.debug.value) + log("Generating SWITCH statement."); + var ctx1 = genLoad(selector, ctx, INT); + val afterCtx = ctx1.newBlock; + var caseCtx: Context = null; + val kind = toTypeKind(tree.tpe); + + var targets: List[BasicBlock] = Nil; + var tags: List[Int] = Nil; + var default: BasicBlock = afterCtx.bb; + + for (val caze <- cases) + caze match { + case CaseDef(Literal(value), EmptyTree, body) => + tags = value.intValue :: tags; + val tmpCtx = ctx1.newBlock; + targets = tmpCtx.bb :: targets; + + caseCtx = genLoad(body, tmpCtx , kind); + caseCtx.bb.emit(JUMP(afterCtx.bb), caze.pos); + caseCtx.bb.close; + + case CaseDef(Ident(nme.WILDCARD), EmptyTree, body) => + val tmpCtx = ctx1.newBlock; + default = tmpCtx.bb; + + caseCtx = genLoad(body, tmpCtx , kind); + caseCtx.bb.emit(JUMP(afterCtx.bb), caze.pos); + caseCtx.bb.close; + + case _ => abort("Invalid case statement in switch-like pattern match: " + + tree + " at: " + unit.position(tree.pos)); + } + ctx1.bb.emit(SWITCH(tags.reverse map (x => List(x)), + (default :: targets).reverse), tree.pos); + ctx1.bb.close; + afterCtx + + case EmptyTree => ctx; + + case _ => abort("Unexpected tree in genLoad: " + tree + " at: " + unit.position(tree.pos)); + } + + // emit conversion + if (generatedType != expectedType) + adapt(generatedType, expectedType, resCtx, tree); + + resCtx; + } + + private def adapt(from: TypeKind, to: TypeKind, ctx: Context, tree: Tree): Unit = { + if (!(from <:< to) && !(from == SCALA_ALLREF && to == SCALA_ALL)) { + to match { + case UNIT => + ctx.bb.emit(DROP(from), tree.pos); + if (settings.debug.value) + log("Dropped an " + from); + + case _ => + assert(from != UNIT, "Can't convert from UNIT to " + to + + tree + " at: " + unit.position(tree.pos)); + ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), tree.pos); + } + } else if (from == SCALA_ALL) { + ctx.bb.emit(DROP(from)); + ctx.bb.emit(getZeroOf(ctx.method.returnType)); + ctx.bb.emit(RETURN(ctx.method.returnType)); + ctx.bb.enterIgnoreMode; + } else if (from == SCALA_ALLREF) { + ctx.bb.emit(DROP(from)); + ctx.bb.emit(CONSTANT(Constant(null))); + } + } + + /** Load the qualifier of `tree' on top of the stack. */ + private def genLoadQualifier(tree: Tree, ctx: Context): Context = + tree match { + case Select(qualifier, _) => + genLoad(qualifier, ctx, ANY_REF_CLASS); // !! + case _ => + abort("Unknown qualifier " + tree); + } + + /** Is this symbol static in the Java sense? */ + def isStaticSymbol(s: Symbol): Boolean = + s.hasFlag(Flags.STATIC) || s.hasFlag(Flags.STATICMEMBER) || s.owner.isImplClass; + + /** + * Generate code that loads args into label parameters. + */ + private def genLoadLabelArguments(args: List[Tree], label: Label, ctx: Context): Context = { + assert(args.length == label.params.length, + "Wrong number of arguments in call to label " + label.symbol); + var ctx1 = ctx; + var arg = args; + var param = label.params; + + // store arguments in reverse order on the stack + while (arg != Nil) { + val Some(l) = ctx.method.lookupLocal(param.head); + ctx1 = genLoad(arg.head, ctx1, l.kind); + arg = arg.tail; + param = param.tail; + } + + // store arguments in the right variables + arg = args.reverse; param = label.params.reverse; + while (arg != Nil) { + val Some(l) = ctx.method.lookupLocal(param.head); + ctx1.bb.emit(STORE_LOCAL(l, param.head.isValueParameter), arg.head.pos); + arg = arg.tail; + param = param.tail; + } + + ctx1 + } + + private def genLoadArguments(args: List[Tree], tpes: List[Type], ctx: Context): Context = { + assert(args.length == tpes.length, "Wrong number of arguments in call " + ctx); + + var ctx1 = ctx; + var arg = args; + var tpe = tpes; + while (arg != Nil) { + ctx1 = genLoad(arg.head, ctx1, toTypeKind(tpe.head)); + arg = arg.tail; + tpe = tpe.tail; + } + ctx1 + } + + def genConversion(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean) = { + if (cast) + ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to))); + else { + ctx.bb.emit(DROP(from)); + ctx.bb.emit(CONSTANT(Constant(from == to))); + } + } + + /** Generate a conversion from a reference type to a value type, like in + * Any -> Array[Int] or Any -> Int + */ + def genBoxedConversion(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean) = { + assert(to.isValueType || to.isArrayType, "Expecting conversion to value type: " + to); + + val boxedCls = to match { + case ARRAY(ARRAY(_)) | ARRAY(REFERENCE(_)) => + definitions.BoxedObjectArrayClass; + case ARRAY(elem) => + definitions.boxedArrayClass(elem.toType.symbol) + case _ => + definitions.boxedClass(to.toType.symbol); + } + + if (cast) { + ctx.bb.emit(CHECK_CAST(REFERENCE(boxedCls))); + ctx.bb.emit(CONSTANT(Constant(definitions.signature(to.toType)))); + ctx.bb.emit(CALL_METHOD(definitions.getMember(boxedCls, "unbox"), + Dynamic)); + } else { + ctx.bb.emit(IS_INSTANCE(REFERENCE(boxedCls))); + } + } + + def genCast(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean) = { + if (cast) + ctx.bb.emit(CHECK_CAST(to)); + else + ctx.bb.emit(IS_INSTANCE(to)); + } + + def zeroOf(k: TypeKind): Tree = k match { + case UNIT => Literal(()); + case BOOL => Literal(false); + case BYTE => Literal(0: Byte); + case SHORT => Literal(0: Short); + case CHAR => Literal(0: Char); + case INT => Literal(0: Int); + case LONG => Literal(0: Long); + case FLOAT => Literal(0.0f); + case DOUBLE => Literal(0.0d); + case REFERENCE(cls) => Literal(null: Any); + case ARRAY(elem) => Literal(null: Any); + } + + def getZeroOf(k: TypeKind): Instruction = k match { + case UNIT => CONSTANT(Constant(())); + case BOOL => CONSTANT(Constant(false)); + case BYTE => CONSTANT(Constant(0: Byte)); + case SHORT => CONSTANT(Constant(0: Short)); + case CHAR => CONSTANT(Constant(0: Char)); + case INT => CONSTANT(Constant(0: Int)); + case LONG => CONSTANT(Constant(0: Long)); + case FLOAT => CONSTANT(Constant(0.0f)); + case DOUBLE => CONSTANT(Constant(0.0d)); + case REFERENCE(cls) => CONSTANT(Constant(null: Any)); + case ARRAY(elem) => CONSTANT(Constant(null: Any)); + } + + + /** Is the given symbol a primitive operation? */ + def isPrimitive(fun: Symbol): Boolean = { + import scalaPrimitives._; + + if (scalaPrimitives.isPrimitive(fun)) + scalaPrimitives.getPrimitive(fun) match { + case EQUALS | HASHCODE | TOSTRING => false; + case _ => true; + } + else + false; + } + + def genCoercion(tree: Tree, ctx: Context, code: Int) = { + import scalaPrimitives._; + code match { + case B2B => (); + case B2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, CHAR)), tree.pos); + case B2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, SHORT)), tree.pos); + case B2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, INT)), tree.pos); + case B2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, LONG)), tree.pos); + case B2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, FLOAT)), tree.pos); + case B2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, DOUBLE)), tree.pos); + + case S2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, BYTE)), tree.pos); + case S2S => (); + case S2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, CHAR)), tree.pos); + case S2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, INT)), tree.pos); + case S2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, LONG)), tree.pos); + case S2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, FLOAT)), tree.pos); + case S2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, DOUBLE)), tree.pos); + + case C2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, BYTE)), tree.pos); + case C2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, SHORT)), tree.pos); + case C2C => (); + case C2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, INT)), tree.pos); + case C2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, LONG)), tree.pos); + case C2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, FLOAT)), tree.pos); + case C2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, DOUBLE)), tree.pos); + + case I2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, BYTE)), tree.pos); + case I2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, SHORT)), tree.pos); + case I2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, CHAR)), tree.pos); + case I2I => (); + case I2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, LONG)), tree.pos); + case I2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, FLOAT)), tree.pos); + case I2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, DOUBLE)), tree.pos); + + case L2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, BYTE)), tree.pos); + case L2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, SHORT)), tree.pos); + case L2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, CHAR)), tree.pos); + case L2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, INT)), tree.pos); + case L2L => (); + case L2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, FLOAT)), tree.pos); + case L2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, DOUBLE)), tree.pos); + + case F2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, BYTE)), tree.pos); + case F2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, SHORT)), tree.pos); + case F2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, CHAR)), tree.pos); + case F2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, INT)), tree.pos); + case F2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, LONG)), tree.pos); + case F2F => (); + case F2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, DOUBLE)), tree.pos); + + case D2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, BYTE)), tree.pos); + case D2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, SHORT)), tree.pos); + case D2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, CHAR)), tree.pos); + case D2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, INT)), tree.pos); + case D2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, LONG)), tree.pos); + case D2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, FLOAT)), tree.pos); + case D2D => (); + + case _ => abort("Unknown coercion primitive: " + code); + } + } + + /** Generate string concatenation. */ + def genStringConcat(tree: Tree, ctx: Context): Context = { + val Apply(Select(larg, _), rarg) = tree; + var ctx1 = ctx; + + assert(rarg.length == 1, + "Too many parameters for string concatenation"); + + val concatenations = liftStringConcat(tree); + if (settings.debug.value) + log("Lifted string concatenations for " + tree + "\n to: " + concatenations); + + ctx1.bb.emit(CALL_PRIMITIVE(StartConcat), tree.pos); + for (val elem <- concatenations) { + val kind = toTypeKind(elem.tpe); + ctx1 = genLoad(elem, ctx1, kind); + ctx1.bb.emit(CALL_PRIMITIVE(StringConcat(kind)), elem.pos); + } + ctx1.bb.emit(CALL_PRIMITIVE(EndConcat), tree.pos); + + ctx1; + } + + /** + * Returns a list of trees that each should be concatenated, from + * left to right. It turns a chained call like "a".+("b").+("c") into + * a list of arguments. + */ + def liftStringConcat(tree: Tree): List[Tree] = tree match { + case Apply(fun @ Select(larg, method), rarg) => + if (isPrimitive(fun.symbol) && + scalaPrimitives.getPrimitive(fun.symbol) == scalaPrimitives.CONCAT) + liftStringConcat(larg) ::: rarg + else + List(tree); + case _ => + List(tree); + } + + + /** + * Traverse the tree and store label stubs in the contxt. This is + * necessary to handle forward jumps, because at a label application + * with arguments, the symbols of the corresponding LabelDef parameters + * are not yet known. + * + * Since it is expensive to traverse each method twice, this method is called + * only when forward jumps really happen, and then it re-traverses the whole + * method, scanning for LabelDefs. + * + * TODO: restrict the scanning to smaller subtrees than the whole method. + * It is sufficient to scan the trees of the innermost enclosing block. + */ + private def scanForLabels(tree: Tree, ctx: Context): Unit = + new Traverser() { + override def traverse(tree: Tree): Unit = tree match { + + case LabelDef(name, params, rhs) => + if (!ctx.labels.contains(tree.symbol)) { + ctx.labels += tree.symbol -> (new Label(tree.symbol) setParams(params map (.symbol))); + ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info)))); + } + super.traverse(rhs); + + case _ => super.traverse(tree); + } + } traverse(tree); + + /** + * Generate code for conditional expressions. The two basic blocks + * represent the continuation in case of success/failure of the + * test. + */ + private def genCond(tree: Tree, + ctx: Context, + thenCtx: Context, + elseCtx: Context): Unit = + { + def genComparisonOp(l: Tree, r: Tree, code: Int): Unit = { + val op: TestOp = code match { + case scalaPrimitives.LT => LT; + case scalaPrimitives.LE => LE; + case scalaPrimitives.GT => GT; + case scalaPrimitives.GE => GE; + case scalaPrimitives.ID | scalaPrimitives.EQ => EQ; + case scalaPrimitives.NI | scalaPrimitives.NE => NE; + + case _ => abort("Unknown comparison primitive: " + code); + }; + + val kind = getMaxType(l.tpe :: r.tpe :: Nil); + var ctx1 = genLoad(l, ctx, kind); + ctx1 = genLoad(r, ctx1, kind); + ctx1.bb.emit(CJUMP(thenCtx.bb, elseCtx.bb, op, kind), r.pos); + ctx1.bb.close; + } + + if (settings.debug.value) + log("Entering genCond with tree: " + tree); + + tree match { + case Apply(fun, args) + if isPrimitive(fun.symbol) => + assert(args.length <= 1, + "Too many arguments for primitive function: " + fun.symbol); + val code = scalaPrimitives.getPrimitive(fun.symbol); + + if (code == scalaPrimitives.ZNOT) { + val Select(leftArg, _) = fun; + genCond(leftArg, ctx, elseCtx, thenCtx); + } else if ((code == scalaPrimitives.EQ || + code == scalaPrimitives.NE)) { + val Select(leftArg, _) = fun; + if (toTypeKind(leftArg.tpe).isReferenceType) { + if (code == scalaPrimitives.EQ) + genEqEqPrimitive(leftArg, args.head, ctx, thenCtx, elseCtx); + else + genEqEqPrimitive(leftArg, args.head, ctx, elseCtx, thenCtx); + } + else + genComparisonOp(leftArg, args.head, code); + } else if (scalaPrimitives.isComparisonOp(code)) { + val Select(leftArg, _) = fun; + genComparisonOp(leftArg, args.head, code); + } else { + code match { + case scalaPrimitives.ZAND => + val Select(leftArg, _) = fun; + + val ctxInterm = ctx.newBlock; + genCond(leftArg, ctx, ctxInterm, elseCtx); + genCond(args.head, ctxInterm, thenCtx, elseCtx); + + case scalaPrimitives.ZOR => + val Select(leftArg, _) = fun; + + val ctxInterm = ctx.newBlock; + genCond(leftArg, ctx, thenCtx, ctxInterm); + genCond(args.head, ctxInterm, thenCtx, elseCtx); + + case _ => + var ctx1 = genLoad(tree, ctx, BOOL); + ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL), tree.pos); + ctx1.bb.close; + } + } + + case _ => + var ctx1 = genLoad(tree, ctx, BOOL); + ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL), tree.pos); + ctx1.bb.close; + } + } + + val eqEqTemp: Name = "eqEqTemp$"; + + + /** + * Generate the "==" code for object references. It is equivalent of + * if (l == null) then r == null else l.equals(r); + */ + def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context, thenCtx: Context, elseCtx: Context): Unit = { + var eqEqTempVar: Symbol = null; + var eqEqTempLocal: Local = null; + + ctx.method.lookupLocal(eqEqTemp) match { + case Some(local) => eqEqTempVar = local.sym; eqEqTempLocal = local; + case None => + eqEqTempVar = ctx.method.symbol.newVariable(l.pos, eqEqTemp); + eqEqTempVar.setInfo(definitions.AnyRefClass.typeConstructor); + eqEqTempLocal = new Local(eqEqTempVar, REFERENCE(definitions.AnyRefClass)); + ctx.method.addLocal(eqEqTempLocal); + } + + var ctx1 = genLoad(l, ctx, ANY_REF_CLASS); + ctx1 = genLoad(r, ctx1, ANY_REF_CLASS); + val tmpNullCtx = ctx1.newBlock; + val tmpNonNullCtx = ctx1.newBlock; + ctx1.bb.emit(STORE_LOCAL(eqEqTempLocal, false), l.pos); + ctx1.bb.emit(DUP(ANY_REF_CLASS)); + ctx1.bb.emit(CZJUMP(tmpNullCtx.bb, tmpNonNullCtx.bb, EQ, ANY_REF_CLASS)); + ctx1.bb.close; + + tmpNullCtx.bb.emit(DROP(ANY_REF_CLASS), l.pos); // type of AnyRef + tmpNullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal, false)); + tmpNullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS)); + tmpNullCtx.bb.close; + + tmpNonNullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal, false), l.pos); + tmpNonNullCtx.bb.emit(CALL_METHOD(definitions.Object_equals, Dynamic)); + tmpNonNullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL)); + tmpNonNullCtx.bb.close; + } + + /** + * Add all fields of the given class symbol to the current ICode + * class. + */ + private def addClassFields(ctx: Context, cls: Symbol): Unit = { + assert(ctx.clazz.symbol eq cls, + "Classes are not the same: " + ctx.clazz.symbol + ", " + cls); + + for (val f <- cls.info.decls.elements) + if (!f.isMethod && f.isTerm) + ctx.clazz.addField(new IField(f)); + } + + /** + * Add parameters to the current ICode method. It is assumed the methods + * have been uncurried, so the list of lists contains just one list. + */ + private def addMethodParams(ctx: Context, vparamss: List[List[ValDef]]): Unit = + vparamss match { + case Nil => () + + case vparams :: Nil => + for (val p <- vparams) + ctx.method.addParam(new Local(p.symbol, toTypeKind(p.symbol.info))); + ctx.method.params = ctx.method.params.reverse; + + case _ => + abort("Malformed parameter list: " + vparamss); + } + + /** + * If the block consists of a single unconditional jump, prune + * it by replacing the instructions in the predecessor to jump + * directly to the JUMP target of the block. + */ + def prune(method: IMethod) = { + var changed = false; + var n = 0; + + def prune0(block: BasicBlock): Unit = { + val optCont = block.lastInstruction match { + case JUMP(b) if (b != block) => Some(b); + case _ => None + } + if (block.size == 1 && optCont != None) { + val Some(cont) = optCont; + val pred = block.predecessors; + log("Preds: " + pred + " of " + block); + pred foreach { p => + p.lastInstruction match { + case CJUMP(succ, fail, cond, kind) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, + if (block == succ) + CJUMP(cont, fail, cond, kind) + else if (block == fail) + CJUMP(succ, cont, cond, kind) + else + abort("Could not find block in preds")); + + case CZJUMP(succ, fail, cond, kind) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, + if (block == succ) + CZJUMP(cont, fail, cond, kind) + else if (block == fail) + CZJUMP(succ, cont, cond, kind) + else + abort("Could not find block in preds")); + + case JUMP(b) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, JUMP(cont)); + + case SWITCH(tags, labels) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, + SWITCH(tags, labels map (l => if (l == block) cont else l))); + } + } + if (changed) + method.code.removeBlock(block); + } + } + + do { + changed = false; + n = n + 1; + method.code traverse prune0; + } while (changed); + + if (settings.debug.value) + log("Prune fixpoint reached in " + n + " iterations."); + } + + def getMaxType(ts: List[Type]): TypeKind = { + def maxType(a: TypeKind, b: TypeKind): TypeKind = + a maxType b; + + val kinds = ts map toTypeKind; + kinds reduceLeft maxType; + } + + /** Check weather a given label definition is introduced by the tail call phase + * It is considered to be so if all value parameters of the label are the + * same as the value parameters of the current method. + */ + def isTailCallLabel(tree: LabelDef, ctx: Context) = ( + tree.params.length == ctx.defdef.vparamss.head && + List.forall2(tree.params, ctx.defdef.vparamss.head) + { (x, y) => x.symbol == y.symbol } + ); + + + /////////////////////// Context //////////////////////////////// + + + /** + * The Context class keeps information relative to the current state + * in code generation + */ + class Context { + + /** The current package. */ + var packg: Name = _; + + /** The current class. */ + var clazz: IClass = _; + + /** The current method. */ + var method: IMethod = _; + + /** The current basic block. */ + var bb: BasicBlock = _; + + /** Map from label symbols to label objects. */ + var labels: HashMap[Symbol, Label] = new HashMap(); + + /** Current method definition. */ + var defdef: DefDef = _; + + /** current exception handlers */ + var handlers: List[ExceptionHandler] = Nil; + + var handlerCount = 0; + + override def toString(): String = { + val buf = new StringBuffer(); + buf.append("\tpackage: ").append(packg).append('\n'); + buf.append("\tclazz: ").append(clazz).append('\n'); + buf.append("\tmethod: ").append(method).append('\n'); + buf.append("\tbb: ").append(bb).append('\n'); + buf.append("\tlabels: ").append(labels).append('\n'); + buf.append("\texception handlers: ").append(handlers).append('\n'); + buf.toString() + } + + + def this(other: Context) = { + this(); + this.packg = other.packg; + this.clazz = other.clazz; + this.method = other.method; + this.bb = other.bb; + this.labels = other.labels; + this.defdef = other.defdef; + this.handlers = other.handlers; + this.handlerCount = other.handlerCount; + } + + def setPackage(p: Name): this.type = { + this.packg = p; + this + } + + def setClass(c: IClass): this.type = { + this.clazz = c; + this + } + + def setMethod(m: IMethod): this.type = { + this.method = m; + this + } + + def setBasicBlock(b: BasicBlock): this.type = { + this.bb = b; + this + } + + /** Prepare a new context upon entry into a method */ + def enterMethod(m: IMethod, d: DefDef): Context = { + val ctx1 = new Context(this) setMethod(m); + ctx1.labels = new HashMap(); + ctx1.method.code = new Code(m.symbol.simpleName.toString()); + ctx1.bb = ctx1.method.code.startBlock; + ctx1.defdef = d; + ctx1 + } + + /** Return a new context for a new basic block. */ + def newBlock: Context = { + val block = method.code.newBlock; + handlers foreach (h => h addBlock block); + new Context(this) setBasicBlock block; + } + + /** Create a new exception handler and adds it in the list + * of current exception handlers. + */ + def newHandler(cls: Symbol): ExceptionHandler = { + handlerCount = handlerCount + 1; + val exh = new ExceptionHandler(method, "" + handlerCount, cls); + method.addHandler(exh); + handlers = exh :: handlers; + if (settings.debug.value) + log("added handler: " + exh); + + exh + } + + /** Return a new context for generating code for the given + * exception handler. + */ + def enterHandler(exh: ExceptionHandler): Context = { + val ctx = newBlock; + exh.setStartBlock(ctx.bb); + ctx + } + + def exitHandler(exh: ExceptionHandler): Unit = { + assert(handlerCount > 0 && handlers.head == exh, + "Wrong nesting of exception handlers." + this + " for " + exh); + handlerCount = handlerCount - 1; + handlers = handlers.tail; + if (settings.debug.value) + log("removed handler: " + exh); + + } + + /** Clone the current context */ + def dup: Context = new Context(this); + + /** + * Generate exception handlers for the body. Body is evaluated + * with a context where all the handlers are active. Handlers are + * evaluated in the 'outer' context. + * + * It returns the resulting context, with the same active handlers as + * before the call. Use it like: + * + * <code> ctx.Try( ctx => { + * ctx.bb.emit(...) // protected block + * }, Pair(definitions.ThrowableClass, + * ctx => { + * ctx.bb.emit(...); // exception handler + * }), Pair(AnotherExceptionClass, + * ctx => {... + * } ))</code> + */ + def Try(body: Context => Context, + handlers: List[Pair[Symbol, (Context => Context)]]) = { + val outerCtx = this.dup; + val afterCtx = outerCtx.newBlock; + + val exhs = handlers.map { handler => + val exh = this.newHandler(handler._1); + val ctx1 = handler._2(outerCtx.enterHandler(exh)); + ctx1.bb.emit(JUMP(afterCtx.bb)); + ctx1.bb.close; + exh + } + val bodyCtx = this.newBlock; + + val finalCtx = body(bodyCtx); + + outerCtx.bb.emit(JUMP(bodyCtx.bb)); + outerCtx.bb.close; + + exhs.reverse foreach finalCtx.exitHandler; + + finalCtx.bb.emit(JUMP(afterCtx.bb)); + finalCtx.bb.close; + + afterCtx + } + } + } + + /** + * Represent a label in the current method code. In order + * to support forward jumps, labels can be created without + * having a deisgnated target block. They can later be attached + * by calling `anchor'. + */ + class Label(val symbol: Symbol) { + var anchored = false; + var block: BasicBlock = _; + var params: List[Symbol] = _; + + private var toPatch: List[Instruction] = Nil; + + /** Fix this label to the given basic block. */ + def anchor(b: BasicBlock): Label = { + assert(!anchored, "Cannot anchor an already anchored label!"); + anchored = true; + this.block = b; + this + } + + def setParams(p: List[Symbol]): Label = { + assert(params == null, "Cannot set label parameters twice!"); + params = p; + this + } + + /** Add an instruction that refers to this label. */ + def addCallingInstruction(i: Instruction) = + toPatch = i :: toPatch; + + /** + * Patch the code by replacing pseudo call instructions with + * jumps to the given basic block. + */ + def patch(code: Code): Unit = { + def substMap: Map[Instruction, Instruction] = { + val map = new HashMap[Instruction, Instruction](); + + toPatch foreach (i => map += i -> patch(i)); + map + } + + val map = substMap; + code traverse (.subst(map)); + } + + /** + * Return the patched instruction. If the given instruction + * jumps to this label, replace it with the basic block. Otherwise, + * return the same instruction. Conditional jumps have more than one + * label, so they are replaced only if all labels are anchored. + */ + def patch(instr: Instruction): Instruction = { + assert(anchored, "Cannot patch until this label is anchored: " + this); + + instr match { + case PJUMP(self) + if (self == this) => JUMP(block); + + case PCJUMP(self, failure, cond, kind) + if (self == this && failure.anchored) => + CJUMP(block, failure.block, cond, kind); + + case PCJUMP(success, self, cond, kind) + if (self == this && success.anchored) => + CJUMP(success.block, block, cond, kind); + + case PCZJUMP(self, failure, cond, kind) + if (self == this && failure.anchored) => + CZJUMP(block, failure.block, cond, kind); + + case PCZJUMP(success, self, cond, kind) + if (self == this && success.anchored) => + CZJUMP(success.block, block, cond, kind); + + case _ => instr; + } + } + } + + ///////////////// Fake instructions ////////////////////////// + + /** + * Pseudo jump: it takes a Label instead of a basick block. + * It is used temporarily during code generation. It is replaced + * by a real JUMP instruction when all labels are resolved. + */ + abstract class PseudoJUMP(label: Label) extends Instruction { + override def toString(): String ="PJUMP " + label.symbol.simpleName; + + override def consumed = 0; + override def produced = 0; + + // register with the given label + if (!label.anchored) + label.addCallingInstruction(this); + } + + case class PJUMP(where: Label) extends PseudoJUMP(where); + + case class PCJUMP(success: Label, failure: Label, cond: TestOp, kind: TypeKind) + extends PseudoJUMP(success) { + + if (!failure.anchored) + failure.addCallingInstruction(this); + } + + case class PCZJUMP(success: Label, failure: Label, cond: TestOp, kind: TypeKind) + extends PseudoJUMP(success) { + + if (!failure.anchored) + failure.addCallingInstruction(this); + } + +} + diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala new file mode 100644 index 0000000000..d29a8b5433 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -0,0 +1,31 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.tools.nsc.symtab._; + +/** Glue together ICode parts. + */ +abstract class ICodes extends AnyRef + with Members + with BasicBlocks + with Opcodes + with TypeStacks + with TypeKinds + with ExceptionHandlers + with Primitives + with Linearizers +{ + val global: Global; + + /** The ICode representation of classes */ + var classes: List[IClass] = _; + + def init = { classes = Nil } +} + diff --git a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala new file mode 100644 index 0000000000..7a0b77f6fa --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala @@ -0,0 +1,89 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.tools.nsc.ast._; +import scala.collection.mutable.Stack; + +trait Linearizers: ICodes { + import opcodes._; + + trait Linearizer { + def linearize(c: IMethod): List[BasicBlock]; + } + + /** + * A simple linearizer which predicts all branches to + * take the 'success' branch and tries to schedule those + * blocks immediately after the test. This is in sync with + * how 'while' statements are translated (if the test is + * 'true', the loop continues). + */ + class NormalLinearizer extends Linearizer with WorklistAlgorithm { + type Elem = BasicBlock; + type WList = Stack[Elem]; + + val worklist: WList = new Stack(); + + var blocks: List[BasicBlock] = Nil; + + def linearize(m: IMethod): List[BasicBlock] = { + val b = m.code.startBlock; + blocks = Nil; + + run { + worklist ++= (m.exh map (.startBlock)); + worklist.push(b); + } + + blocks.reverse; + } + + /** Linearize another subtree and append it to the existing blocks. */ + def linearize(startBlock: BasicBlock): List[BasicBlock] = { + //blocks = startBlock :: Nil; + run( { worklist.push(startBlock); } ); + blocks.reverse; + } + + def processElement(b: BasicBlock) = + if (b.size > 0) { + add(b); + b.lastInstruction match { + case JUMP(where) => + add(where); + case CJUMP(success, failure, _, _) => + add(success); + add(failure); + case CZJUMP(success, failure, _, _) => + add(success); + add(failure); + case SWITCH(_, labels) => + add(labels); + case RETURN(_) => (); + case THROW() => (); + } + } + + def dequeue: Elem = worklist.pop; + + /** + * Prepend b to the list, if not already scheduled. + * TODO: use better test than linear search + */ + def add(b: BasicBlock) = + if (blocks.contains(b)) + () + else { + blocks = b :: blocks; + worklist push b; + } + + def add(bs: List[BasicBlock]): Unit = bs foreach add; + } +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala new file mode 100644 index 0000000000..f12c4ef62c --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -0,0 +1,241 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.collection.mutable.HashMap; +import scala.collection.mutable.{Set, HashSet}; +import scala.{Symbol => scala_Symbol}; + +import scala.tools.nsc.symtab.Flags; + +trait Members: ICodes { + import global._; + + /** + * This class represents the intermediate code of a method or + * other multi-block piece of code, like exception handlers. + */ + class Code(label: String) { + + /** The set of all blocks */ + val blocks: HashSet[BasicBlock] = new HashSet; + + /** The start block of the method */ + var startBlock: BasicBlock = null; + + /** The stack produced by this method */ + var producedStack: TypeStack = null; + + private var currentLabel: int = 0; + + // Constructor code + startBlock = newBlock; + startBlock.initStack(new TypeStack); + + + def removeBlock(b: BasicBlock) = { + if (settings.debug.value) { + assert(blocks.forall(p => !(p.successors.contains(b))), + "Removing block that is still referenced in method code " + label); + if (b == startBlock) + assert(b.successors.length == 1, + "Removing start block with more than one successor."); + } + + if (b == startBlock) + startBlock = b.successors.head; + blocks -= b; + } + + /** + * Apply a function to all basic blocks, for side-effects. It starts at + * the given startBlock and checks that are no predecessors of the given node. + * Only blocks that are reachable via a path from startBlock are ever visited. + */ + def traverseFrom(startBlock: BasicBlock, f: BasicBlock => Unit) = { + val visited: Set[BasicBlock] = new HashSet(); + + def traverse0(toVisit: List[BasicBlock]): Unit = toVisit match { + case Nil => (); + case b :: bs => if (!visited.contains(b)) { + f(b); + visited += b; + traverse0(bs ::: b.successors); + } else + traverse0(bs); + } + assert(startBlock.predecessors == Nil, + "Starting traverse from a block with predecessors: " + this); + traverse0(startBlock :: Nil) + } + + def traverse(f: BasicBlock => Unit) = blocks foreach f; + + /* This method applies the given function to each basic block. */ + def traverseFeedBack(f: (BasicBlock, HashMap[BasicBlock, Boolean]) => Unit) = { + val visited : HashMap[BasicBlock, Boolean] = new HashMap; + visited ++= blocks.elements.map(x => Pair(x, false)); + + var blockToVisit : List[BasicBlock] = startBlock::Nil; + + while (!blockToVisit.isEmpty) { + blockToVisit match { + case b::xs => + if (!visited(b)) { + f(b, visited); + blockToVisit = b.successors ::: xs; + visited += b -> true; + } else + blockToVisit = xs; + } + } + } + + /** This methods returns a string representation of the ICode */ + override def toString() : String = "ICode '" + label + "'"; + + /** This method print the code */ +// def print() : unit = print(System.out); + +// def print(out: java.io.PrintStream) : unit = { +// traverse((bb: BasicBlock) => { +// out.println("Block #" + bb.label); +// out.println("Substituable variables : "); +// if (bb.substituteVars != null) +// bb.substituteVars.foreach(out.print); +// else +// out.println(" {Empty} "); +// out.println("Instructions:"); +// bb.traverse((ici: Instruction) => +// out.println(" "+ici.toString())); +// out.print ("Successors: "); +// bb.successors.foreach((bb: BasicBlock) => out.print(bb.label+", ")); +// out.println (""); // ?? Del +// out.println (); +// }); +// } + + /* Compute a unique new label */ + def nextLabel = { + currentLabel = currentLabel + 1; + currentLabel; + } + + /* Create a new block and append it to the list + */ + def newBlock: BasicBlock = { + val block = new BasicBlock(nextLabel, this); + blocks += block; + block; + } + } + + /** Represent a class in ICode */ + class IClass(val symbol: Symbol) { + var fields: List[IField] = Nil; + var methods: List[IMethod] = Nil; + var cunit: CompilationUnit = _; + + def addField(f: IField): this.type = { + fields = f :: fields; + this + } + + def addMethod(m: IMethod): this.type = { + methods = m :: methods; + this + } + + def setCompilationUnit(unit: CompilationUnit): this.type = { + this.cunit = unit; + this + } + + override def toString() = symbol.fullNameString; + + def lookupField(s: Symbol) = fields find ((f) => f.symbol == s); + } + + /** Represent a field in ICode */ + class IField(val symbol: Symbol) { + } + + /** + * Represents a method in ICode. Local variables contain + * both locals and parameters, similar to the way the JVM + * 'sees' them. + * + * Locals and parameters are added in reverse order, as they + * are kept in cons-lists. The 'builder' is responsible for + * reversing them and putting them back, when the generation is + * finished (GenICode does that). + */ + class IMethod(val symbol: Symbol) { + var code: Code = null; + var exh: List[ExceptionHandler] = Nil; + var sourceFile: String = _; + var returnType: TypeKind = _; + + /** local variables and method parameters */ + var locals: List[Local] = Nil; + + /** method parameters */ + var params: List[Local] = Nil; + + def setCode(code: Code): IMethod = { + this.code = code; + this + } + + def addLocal(l: Local): Unit = + if (!(locals contains l)) + locals = l :: locals; + + def addLocals(ls: List[Local]): Unit = + ls foreach addLocal; + + def addParam(p: Local): Unit = + if (!(params contains p)) { + params = p :: params; + locals = p :: locals; + } + + def addParams(as: List[Local]): Unit = + as foreach addParam; + + def lookupLocal(n: Name): Option[Local] = + locals find ((l) => l.sym.name == n); + + def lookupLocal(sym: Symbol): Option[Local] = + locals find ((l) => l.sym == sym); + + def addHandler(e: ExceptionHandler): Unit = + exh = e :: exh; + + /** Is this method deferred ('abstract' in Java sense) */ + def isDeferred = ( + symbol.hasFlag(Flags.DEFERRED) || + symbol.owner.hasFlag(Flags.INTERFACE) + ); + + override def toString() = symbol.fullNameString; + } + + /** Represent local variables and parameters */ + class Local(val sym: Symbol, val kind: TypeKind) { + var index: Int = -1; + + override def equals(other: Any): Boolean = ( + other.isInstanceOf[Local] && + other.asInstanceOf[Local].sym == this.sym + ); + + override def toString(): String = sym.toString(); + } + +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala new file mode 100644 index 0000000000..24eb1132f6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -0,0 +1,473 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + + +package scala.tools.nsc.backend.icode; + +import scala.tools.nsc.ast._; +import scala.tools.nsc.util.Position; + +/* + A pattern match + + case THIS(clasz) => + case CONSTANT(const) => + case LOAD_ARRAY_ITEM(kind) => + case LOAD_LOCAL(local, isArg) => + case LOAD_FIELD(field, isStatic) => + case LOAD_MODULE(module) => + case STORE_ARRAY_ITEM(kind) => + case STORE_LOCAL(local, isArg) => + case STORE_FIELD(field, isStatic) => + case CALL_PRIMITIVE(primitive) => + case CALL_METHOD(method, style) => + case NEW(kind) => + case CREATE_ARRAY(elem) => + case IS_INSTANCE(tpe) => + case CHECK_CAST(tpe) => + case SWITCH(tags, labels) => + case JUMP(where) => + case CJUMP(success, failure, cond, kind) => + case CZJUMP(success, failure, cond, kind) => + case RETURN(kind) => + case THROW() => + case DROP(kind) => + case DUP(kind) => + case MONITOR_ENTER() => + case MONITOR_EXIT() => +*/ + + +/** + * The ICode intermediate representation. It is a stack-based + * representation, very close to the JVM and .NET. It uses the + * erased types of Scala and references Symbols to refer named entities + * in the source files. + */ +[_trait_] abstract class Opcodes: ICodes { + import global.{Symbol, NoSymbol, Type, Name, Constant}; + + /** This class represents an instruction of the intermediate code. + * Each case subclass will represent a specific operation. + */ + abstract class Instruction { + + /** This abstract method returns the number of used elements on the stack */ + def consumed : Int = 0; + + /** This abstract method returns the number of produced elements on the stack */ + def produced : Int = 0; + + /** This method returns the difference of size of the stack when the instruction is used */ + def difference = produced-consumed; + + /** The corresponding position in the source file */ + var pos: Int = Position.NOPOS; + } + + object opcodes { + + /** Loads the "this" references on top of the stack. + * Stack: ... + * ->: ...:ref + */ + case class THIS(clasz: Symbol) extends Instruction { + /** Returns a string representation of this constant */ + override def toString(): String = "THIS"; + + override def consumed = 0; + override def produced = 1; + } + + /** Loads a constant on the stack. + * Stack: ... + * ->: ...:constant + */ + case class CONSTANT(constant: Constant) extends Instruction{ + /** Returns a string representation of this constant */ + override def toString(): String = "CONSTANT ("+constant.toString()+")"; + + override def consumed = 0; + override def produced = 1; + } + + /** Loads an element of an array. The array and the index should + * be on top of the stack. + * Stack: ...:array[a](Ref):index(Int) + * ->: ...:element(a) + */ + case class LOAD_ARRAY_ITEM(kind: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "LOAD_ARRAY_ITEM (" + kind + ")"; + + override def consumed = 2; + override def produced = 1; + } + + /** Load a local variable on the stack. It can be a method argument. + * Stack: ... + * ->: ...:value + */ + case class LOAD_LOCAL(local: Local, isArgument: boolean) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "LOAD_LOCAL "+local.toString(); //+isArgument?" (argument)":""; + + override def consumed = 0; + override def produced = 1; + } + + /** Load a field on the stack. The object to which it refers should be + * on the stack. + * Stack: ...:ref + * ->: ...:value + */ + case class LOAD_FIELD(field: Symbol, isStatic: boolean) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = + "LOAD_FIELD " + (if (isStatic) field.fullNameString else field.toString()); + + override def consumed = 1; + override def produced = 1; + } + + case class LOAD_MODULE(module: Symbol) extends Instruction { + assert(module != NoSymbol, + "Invalid module symbol"); + /** Returns a string representation of this instruction */ + override def toString(): String = + "LOAD_MODULE " + module.toString(); + + override def consumed = 0; + override def produced = 1; + } + + /** Store a value into an array at a specified index. + * Stack: ...:array[a](Ref):index(Int):value(a) + * ->: ... + */ + case class STORE_ARRAY_ITEM(kind: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "STORE_ARRAY_ITEM (" + kind + ")"; + + override def consumed = 3; + override def produced = 0; + } + + /** Store a value into a local variable. It can be an argument. + * Stack: ...:value + * ->: ... + */ + case class STORE_LOCAL(local: Local, isArgument: boolean) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "STORE_LOCAL "+local.toString(); //+isArgument?" (argument)":""; + + override def consumed = 1; + override def produced = 0; + } + + /** Store a value into a field. + * Stack: ...:ref:value + * ->: ... + */ + case class STORE_FIELD(field: Symbol, isStatic: boolean) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "STORE_FIELD "+field.toString(); //+isStatic?" (static)":""; + + override def consumed = 2; + override def produced = 0; + } + + /** Call a primitive function. + * Stack: ...:arg1:arg2:...:argn + * ->: ...:result + */ + case class CALL_PRIMITIVE(primitive: Primitive) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="CALL_PRIMITIVE "+primitive.toString(); + + override def consumed = primitive match { + case Negation(_) => 1; + case Test(_,_,true) => 1; + case Test(_,_,false) => 2; + case Comparison(_,_) => 2; + case Arithmetic(_,_) => 2; + case Logical(_,_) => 2; + case Shift(_,_) => 2; + case Conversion(_,_) => 1; + case ArrayLength(_) => 1; + case StringConcat(_) => 2; + case StartConcat => 0; + case EndConcat => 1; + } + override def produced = 1; + } + + /** This class represents a CALL_METHOD instruction + * STYLE: dynamic / static(StaticInstance) + * Stack: ...:ref:arg1:arg2:...:argn + * ->: ...:result + * + * STYLE: static(StaticClass) + * Stack: ...:arg1:arg2:...:argn + * ->: ...:result + * + */ + case class CALL_METHOD(method: Symbol, style: InvokeStyle) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = + "CALL_METHOD " + method.fullNameString +" ("+style.toString()+")"; + + override def consumed = { + var result = method.tpe.paramTypes.length; + result = result + (style match { + case Dynamic => 1 + case Static(true) => 1 + case _ => 0 + }); + + result; + } + override def produced = 1; + } + + /** Create a new instance of a class through the specified constructor + * Stack: ...:arg1:arg2:...:argn + * ->: ...:ref + */ + case class NEW(kind: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "NEW "+ kind; + + override def consumed = 0; + override def produced = 1; + } + + + /** This class represents a CREATE_ARRAY instruction + * Stack: ...:size(int) + * ->: ...:arrayref + */ + case class CREATE_ARRAY(element: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="CREATE_ARRAY "+element.toString(); + + override def consumed = 1; + override def produced = 1; + } + + /** This class represents a IS_INSTANCE instruction + * Stack: ...:ref + * ->: ...:result(boolean) + */ + case class IS_INSTANCE(typ: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="IS_INSTANCE "+typ.toString(); + + override def consumed = 1; + override def produced = 1; + } + + /** This class represents a CHECK_CAST instruction + * Stack: ...:ref(oldtype) + * ->: ...:ref(typ <=: oldtype) + */ + case class CHECK_CAST(typ: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="CHECK_CAST "+typ.toString(); + + override def consumed = 1; + override def produced = 1; + } + + /** This class represents a SWITCH instruction + * Stack: ...:index(int) + * ->: ...: + * + * The tags array contains one entry per label, each entry consisting of + * an array of ints, any of which will trigger the jump to the corresponding label. + * labels should contain an extra label, which is the 'default' jump. + */ + case class SWITCH(tags: List[List[Int]], labels: List[BasicBlock]) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="SWITCH ..."; + + override def consumed = 1; + override def produced = 0; + } + + /** This class represents a JUMP instruction + * Stack: ... + * ->: ... + */ + case class JUMP(where: BasicBlock) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="JUMP "+where.label; + + override def consumed = 0; + override def produced = 0; + } + + /** This class represents a CJUMP instruction + * It compares the two values on the stack with the 'cond' test operator + * Stack: ...:value1:value2 + * ->: ... + */ + case class CJUMP(successBlock: BasicBlock, + failureBlock: BasicBlock, + cond: TestOp, + kind: TypeKind) extends Instruction + { + + /** Returns a string representation of this instruction */ + override def toString(): String = ( + "CJUMP (" + kind + ")" + + cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label + ); + + override def consumed = 2; + override def produced = 0; + } + + /** This class represents a CZJUMP instruction + * It compares the one value on the stack and zero with the 'cond' test operator + * Stack: ...:value: + * ->: ... + */ + case class CZJUMP(successBlock: BasicBlock, + failureBlock: BasicBlock, + cond: TestOp, + kind: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = ( + "CZJUMP (" + kind + ")" + + cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label + ); + + override def consumed = 1; + override def produced = 0; + } + + + /** This class represents a RETURN instruction + * Stack: ... + * ->: ... + */ + case class RETURN(kind: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="RETURN (" + kind + ")"; + + override def consumed = 0; + override def produced = 0; + } + + /** This class represents a THROW instruction + * Stack: ...:Throwable(Ref) + * ->: ...: + */ + case class THROW() extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="THROW"; + + override def consumed = 1; + override def produced = 0; + } + + /** This class represents a DROP instruction + * Stack: ...:something + * ->: ... + */ + case class DROP (typ: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="DROP "+typ.toString(); + + override def consumed = 1; + override def produced = 0; + } + + /** This class represents a DUP instruction + * Stack: ...:something + * ->: ...:something:something + */ + case class DUP (typ: TypeKind) extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="DUP"; + + override def consumed = 1; + override def produced = 2; + } + + /** This class represents a MONITOR_ENTER instruction + * Stack: ...:object(ref) + * ->: ...: + */ + case class MONITOR_ENTER() extends Instruction { + + /** Returns a string representation of this instruction */ + override def toString(): String ="MONITOR_ENTER"; + + override def consumed = 1; + override def produced = 0; + } + + /** This class represents a MONITOR_EXIT instruction + * Stack: ...:object(ref) + * ->: ...: + */ + case class MONITOR_EXIT() extends Instruction { + + /** Returns a string representation of this instruction */ + override def toString(): String ="MONITOR_EXIT"; + + override def consumed = 1; + override def produced = 0; + } + + /** This class represents a method invocation style. */ + trait InvokeStyle { + + /** Is this a dynamic method call? */ + def isDynamic: Boolean = this match { + case Dynamic => true; + case _ => false; + } + + /** Is this a static method call? */ + def isStatic: Boolean = this match { + case Static(_) => true; + case _ => false; + } + + /** Is this an instance method call? */ + def hasInstance: Boolean = this match { + case Dynamic => true; + case Static(onInstance) => onInstance; + case SuperCall(_) => true; + case _ => false; + } + + /** Returns a string representation of this style. */ + override def toString(): String = this match { + case Dynamic => "dynamic"; + case Static(false) => "static-class"; + case Static(true) => "static-instance"; + case SuperCall(mixin) => "super(" + mixin + ")"; + } + } + + case object Dynamic extends InvokeStyle; + + /** + * Special invoke. Static(true) is used for calls to private + * members. + */ + case class Static(onInstance: Boolean) extends InvokeStyle; + + /** Call through super[mixin]. */ + case class SuperCall(mixin: Name) extends InvokeStyle; + + } +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala b/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala new file mode 100644 index 0000000000..5cebb0edaf --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala @@ -0,0 +1,239 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import java.io.PrintWriter; + +[_trait_] abstract class Primitives: ICodes { + + /** This class represents a primitive operation. */ + class Primitive { + } + + + // type : (type) => type + // range: type <- { BOOL, Ix, Ux, Rx } + // jvm : {i, l, f, d}neg + case class Negation(kind: TypeKind) extends Primitive; + + // type : zero ? (type) => BOOL : (type,type) => BOOL + // range: type <- { BOOL, Ix, Ux, Rx, REF } + // jvm : if{eq, ne, lt, ge, le, gt}, if{null, nonnull} + // if_icmp{eq, ne, lt, ge, le, gt}, if_acmp{eq,ne} + case class Test(op: TestOp, kind: TypeKind, zero: boolean) extends Primitive; + + // type : (type,type) => I4 + // range: type <- { Ix, Ux, Rx } + // jvm : lcmp, {f, d}cmp{l, g} + case class Comparison(op: ComparisonOp, kind: TypeKind) extends Primitive; + + // type : (type,type) => type + // range: type <- { Ix, Ux, Rx } + // jvm : {i, l, f, d}{add, sub, mul, div, rem} + case class Arithmetic(op: ArithmeticOp, kind: TypeKind) extends Primitive; + + // type : (type,type) => type + // range: type <- { BOOL, Ix, Ux } + // jvm : {i, l}{and, or, xor} + case class Logical(op: LogicalOp, kind: TypeKind) extends Primitive; + + // type : (type,I4) => type + // range: type <- { Ix, Ux } + // jvm : {i, l}{shl, ushl, shr} + case class Shift(op: ShiftOp, kind: TypeKind) extends Primitive; + + // type : (src) => dst + // range: src,dst <- { Ix, Ux, Rx } + // jvm : i2{l, f, d}, l2{i, f, d}, f2{i, l, d}, d2{i, l, f}, i2{b, c, s} + case class Conversion(src: TypeKind, dst: TypeKind) extends Primitive; + + // type : (Array[REF]) => I4 + // range: type <- { BOOL, Ix, Ux, Rx, REF } + // jvm : arraylength + case class ArrayLength(kind: TypeKind) extends Primitive; + + // type : (buf,el) => buf + // range: lf,rg <- { BOOL, Ix, Ux, Rx, REF, STR } + // jvm : It should call the appropiate 'append' method on StringBuffer + case class StringConcat(el: TypeKind) extends Primitive; + + /** Signals the beginning of a series of concatenations. + * On the JVM platform, it should create a new StringBuffer + */ + case object StartConcat extends Primitive; + + /** + * type: (buf) => STR + * jvm : It should turn the StringBuffer into a String. + */ + case object EndConcat extends Primitive; + + /** Pretty printer for primitives */ + class PrimitivePrinter(out: PrintWriter) { + + def print(s: String): PrimitivePrinter = { + out.print(s); + this + } + + def print(o: AnyRef): PrimitivePrinter = print(o.toString()); + + def printPrimitive(prim: Primitive) = prim match { + case Negation(kind) => + print("!"); + + case Test(op, kind, zero) => + print(op).print(kind); + + case Comparison(op, kind) => + print(op).print("(").print(kind); + + } + } + + /** This class represents a comparison operation. */ + class ComparisonOp { + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case CMPL => "CMPL"; + case CMP => "CMP"; + case CMPG => "CMPG"; + case _ => throw new RuntimeException("ComparisonOp unknown case"); + } + } + + /** A comparison operation with -1 default for NaNs */ + case object CMPL extends ComparisonOp; + + /** A comparison operation with no default for NaNs */ + case object CMP extends ComparisonOp; + + /** A comparison operation with +1 default for NaNs */ + case object CMPG extends ComparisonOp; + + + /** This class represents a test operation. */ + class TestOp { + + /** Returns the negation of this operation. */ + def negate(): TestOp = this match { + case EQ => NE; + case NE => EQ; + case LT => GE; + case GE => LT; + case LE => GT; + case GT => LE; + case _ => throw new RuntimeException("TestOp unknown case"); + } + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case EQ => "EQ"; + case NE => "NE"; + case LT => "LT"; + case GE => "GE"; + case LE => "LE"; + case GT => "GT"; + case _ => throw new RuntimeException("TestOp unknown case"); + } + } + /** An equality test */ + case object EQ extends TestOp; + + /** A non-equality test */ + case object NE extends TestOp; + + /** A less-than test */ + case object LT extends TestOp; + + /** A greater-than-or-equal test */ + case object GE extends TestOp; + + /** A less-than-or-equal test */ + case object LE extends TestOp; + + /** A greater-than test */ + case object GT extends TestOp; + + /** This class represents an arithmetic operation. */ + class ArithmeticOp { + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case ADD => "ADD"; + case SUB => "SUB"; + case MUL => "MUL"; + case DIV => "DIV"; + case REM => "REM"; + case NOT => "NOT"; + case _ => throw new RuntimeException("ArithmeticOp unknown case"); + } + } + + /** An arithmetic addition operation */ + case object ADD extends ArithmeticOp; + + /** An arithmetic subtraction operation */ + case object SUB extends ArithmeticOp; + + /** An arithmetic multiplication operation */ + case object MUL extends ArithmeticOp; + + /** An arithmetic division operation */ + case object DIV extends ArithmeticOp; + + /** An arithmetic remainder operation */ + case object REM extends ArithmeticOp; + + /** Bitwise negation. */ + case object NOT extends ArithmeticOp; + + /** This class represents a shift operation. */ + class ShiftOp { + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case LSL => "LSL"; + case ASR => "ASR"; + case LSR => "LSR"; + case _ => throw new RuntimeException("ShitOp unknown case"); + } + } + + /** A logical shift to the left */ + case object LSL extends ShiftOp; + + /** An arithmetic shift to the right */ + case object ASR extends ShiftOp; + + /** A logical shift to the right */ + case object LSR extends ShiftOp; + + /** This class represents a logical operation. */ + class LogicalOp { + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case AND => return "AND"; + case OR => return "OR"; + case XOR => return "XOR"; + case _ => throw new RuntimeException("LogicalOp unknown case"); + } + } + + /** A bitwise AND operation */ + case object AND extends LogicalOp; + + /** A bitwise OR operation */ + case object OR extends LogicalOp; + + /** A bitwise XOR operation */ + case object XOR extends LogicalOp; +} + diff --git a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala new file mode 100644 index 0000000000..dbc7badb9a --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala @@ -0,0 +1,128 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import java.io.PrintWriter; + +import scala.tools.nsc.util.Position; +import scala.tools.nsc.symtab.Flags; + +abstract class Printers { + val global: Global; + import global._; + import global.icodes.opcodes._; + import global.icodes._; + + class TextPrinter(writer: PrintWriter) { + var margin = 0; + var out = writer; + val linearizer = new NormalLinearizer(); + + final val TAB = 2; + + def setWriter(w: PrintWriter) = (out = w); + + def indent = margin = margin + TAB; + def undent = margin = margin - TAB; + + def print(s: String) = out.print(s); + def print(o: Any): Unit = print(o.toString()); + + def println(s: String): Unit = { + print(s); + println + } + + def println = { + out.println(); + var i = 0; + while (i < margin) { + print(" "); + i = i + 1; + } + } + + def printList[a](l: List[a], sep: String): Unit = l match { + case Nil => (); + case x :: Nil => print(x); + case x :: xs => print(x); print(sep); printList(xs, sep); + } + + def printList[a](pr: a => Unit)(l: List[a], sep: String): Unit = l match { + case Nil => (); + case x :: Nil => pr(x); + case x :: xs => pr(x); print(sep); printList(pr)(xs, sep); + } + + + private var clazz : IClass = _; + def printClass(cls: IClass): Unit = { + this.clazz = cls; + print(cls.symbol.toString()); print(" extends "); + printList(cls.symbol.info.parents, ", "); + indent; println(" {"); + println("// fields:"); + cls.fields.foreach(printField); println; + println("// methods"); + cls.methods.foreach(printMethod); + undent; println; + println("}"); + } + + def printField(f: IField): Unit = { + print(f.symbol.keyString); print(" "); + print(f.symbol.nameString); print(": "); + println(f.symbol.info.toString()); + } + + def printMethod(m: IMethod): Unit = { + print("def "); print(m.symbol.name); + print("("); printList(printParam)(m.params.reverse, ", "); print(")"); + print(": "); print(m.symbol.info.resultType); + + if (!m.isDeferred) { + println(" {"); + println("locals: " + m.locals.mkString("", ", ", "")); + println; + linearizer.linearize(m) foreach printBlock; + println("}"); + + indent;println("Exception handlers: "); + m.exh foreach printExceptionHandler; + + undent;println; + } else + println; + } + + def printParam(p: Local): Unit = { + print(p.sym.name); print(": "); print(p.sym.info); + print(" ("); print(p.kind); print(")"); + } + + def printExceptionHandler(e: ExceptionHandler) = { + indent; + println("catch (" + e.cls.simpleName + ") in " + e.covered + " starting at: " + e.startBlock); + undent; + println("with finalizer: " + e.finalizer); +// linearizer.linearize(e.startBlock) foreach printBlock; + } + + def printBlock(bb: BasicBlock): Unit = { + print(bb.label); print(": "); indent; println; + bb traverse printInstruction; + undent; println; + } + + def printInstruction(i: Instruction): Unit = { + if (settings.debug.value) + print("/* " + Position.line(clazz.cunit.source, i.pos) + " */ "); + println(i.toString()); + } + } +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala new file mode 100644 index 0000000000..c1f0c204c6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -0,0 +1,352 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +/* A type case + + case UNIT => + case BOOL => + case BYTE => + case SHORT => + case CHAR => + case INT => + case LONG => + case FLOAT => + case DOUBLE => + case REFERENCE(cls) => + case ARRAY(elem) => + +*/ + +import scala.collection.mutable.{Map, HashMap}; + +[_trait_] abstract class TypeKinds: ICodes { + import global._; + + /** This class represents a type kind. Type kinds + * represent the types that the VM know (or the ICode + * view of what VMs know). + */ + abstract class TypeKind { + + /** Returns a string representation of this type kind. */ + override def toString(): String = this match { + case UNIT => "UNIT"; + case BOOL => "BOOL"; + case BYTE => "BYTE" + case SHORT => "SHORT"; + case CHAR => "CHAR"; + case INT => "INT"; + case LONG => "LONG"; + case FLOAT => "FLOAT"; + case DOUBLE => "DOUBLE"; + case REFERENCE(cls) => "REFERENCE(" + cls.fullNameString + ")" ; + case ARRAY(elem) => "ARRAY[" + elem + "]"; + case _ => abort("Unkown type kind."); + } + + def toType: Type = this match { + case UNIT => definitions.UnitClass.info; + case BOOL => definitions.BooleanClass.info; + case BYTE => definitions.ByteClass.info; + case SHORT => definitions.ShortClass.info; + case CHAR => definitions.CharClass.info; + case INT => definitions.IntClass.info; + case LONG => definitions.LongClass.info; + case FLOAT => definitions.FloatClass.info; + case DOUBLE => definitions.DoubleClass.info; + case REFERENCE(cls) => typeRef(cls.typeConstructor.prefix, cls, Nil); + case ARRAY(elem) => typeRef(definitions.ArrayClass.typeConstructor.prefix, + definitions.ArrayClass, + elem.toType :: Nil); + case _ => abort("Unknown type kind."); + } + + def isReferenceType: Boolean = false; + def isArrayType: Boolean = false; + def isValueType: Boolean = !isReferenceType && !isArrayType; + + + def isIntType: Boolean = this match { + case BYTE | SHORT | INT | LONG | CHAR => true; + case _ => false; + } + + def isRealType: Boolean = this match { + case FLOAT | DOUBLE => true; + case _ => false; + } + + def isNumericType: Boolean = isIntType | isRealType; + + def maxType(other: TypeKind): TypeKind; + + /** Simple subtyping check */ + def <:<(other: TypeKind): Boolean = (this == other); + + override def equals(other: Any): Boolean = + this eq other.asInstanceOf[AnyRef]; + } + + /** + * The least upper bound of two typekinds. They have to be either + * REFERENCE or ARRAY kinds. + * + * The lub is based on the lub of scala types. + */ + def lub(a: TypeKind, b: TypeKind): TypeKind = { + def lub0(t1: Type, t2: Type): Type = { + val lubTpe = global.lub(t1 :: t2 :: Nil); + assert(lubTpe.symbol.isClass, + "Least upper bound of " + t1 + " and " + t2 + " is not a class: " + lubTpe); + lubTpe; + } + + if ((a.isReferenceType || a.isArrayType) && + (b.isReferenceType || b.isArrayType)) + toTypeKind(lub0(a.toType, b.toType)) + else if (a == b) a + else if (a == REFERENCE(definitions.AllClass)) b + else if (b == REFERENCE(definitions.AllClass)) a + else throw new CheckerError("Incompatible types: " + a + " with " + b); + } + + /** The unit value */ + case object UNIT extends TypeKind { + def maxType(other: TypeKind): TypeKind = other match { + case UNIT => UNIT; + case _ => abort("Uncomparbale type kinds: UNIT with " + other); + } + } + + /** A boolean value */ + case object BOOL extends TypeKind { + override def maxType(other: TypeKind): TypeKind = other match { + case BOOL => BOOL; + case _ => abort("Uncomparbale type kinds: BOOL with " + other); + } + } + + /** A 1-byte signed integer */ + case object BYTE extends TypeKind { + override def maxType(other: TypeKind): TypeKind = + other match { + case INT => INT; + case BYTE | SHORT | INT | LONG | FLOAT | DOUBLE => other; + case _ => abort("Uncomparbale type kinds: BYTE with " + other); + } + } + + /** A 2-byte signed integer */ + case object SHORT extends TypeKind { + override def maxType(other: TypeKind): TypeKind = + other match { + case BYTE | SHORT | CHAR => SHORT; + case INT | LONG | FLOAT | DOUBLE => other; + case _ => abort("Uncomparbale type kinds: SHORT with " + other); + } + } + + /** A 2-byte signed integer */ + case object CHAR extends TypeKind { + override def maxType(other: TypeKind): TypeKind = + other match { + case BYTE | SHORT | CHAR => CHAR; + case INT | LONG | FLOAT | DOUBLE => other; + case _ => abort("Uncomparbale type kinds: CHAR with " + other); + } + } + + + /** A 4-byte signed integer */ + case object INT extends TypeKind { + override def maxType(other: TypeKind): TypeKind = + other match { + case BYTE | SHORT | CHAR | INT => INT; + case LONG | FLOAT | DOUBLE => other; + case _ => abort("Uncomparbale type kinds: INT with " + other); + } + } + + /** An 8-byte signed integer */ + case object LONG extends TypeKind { + override def maxType(other: TypeKind): TypeKind = + other match { + case BYTE | SHORT | CHAR | INT | LONG => LONG; + case FLOAT | DOUBLE => DOUBLE; + case _ => abort("Uncomparbale type kinds: LONG with " + other); + } + } + + /** A 4-byte floating point number */ + case object FLOAT extends TypeKind { + override def maxType(other: TypeKind): TypeKind = other match { + case BYTE | SHORT | CHAR | INT | FLOAT => FLOAT; + case DOUBLE => DOUBLE; + case _ => abort("Uncomparbale type kinds: FLOAT with " + other); + } + } + + /** An 8-byte floating point number */ + case object DOUBLE extends TypeKind { + override def maxType(other: TypeKind): TypeKind = + if (other.isNumericType) + DOUBLE; + else + abort("Uncomparbale type kinds: DOUBLE with " + other); + } + + /** A string reference */ + // case object STRING extends TypeKind { + // override def maxType(other: TypeKind): TypeKind = other match { + // case STRING => STRING; + // case _ => + // abort("Uncomparbale type kinds: STRING with " + other); + // } + // } + + /** A class type. */ + case class REFERENCE(cls: Symbol) extends TypeKind { + assert(cls != null, + "REFERENCE to null class symbol."); + assert(cls != definitions.ArrayClass, + "REFERENCE to Array is not allowed, should be ARRAY[..] instead"); + + override def toString(): String = + "REFERENCE(" + cls.fullNameString + ")"; + + /** + * Approximate `lub'. The common type of two references is + * always AnyRef. For 'real' least upper bound wrt to subclassing + * use method 'lub'. + */ + override def maxType(other: TypeKind): TypeKind = + other match { + case REFERENCE(_) => REFERENCE(definitions.AnyRefClass); + case _ => + abort("Uncomparbale type kinds: REFERENCE with " + other); + } + + /** Checks subtyping relationship. */ + override def <:<(other: TypeKind): Boolean = + if (cls == definitions.AllClass) + true + else other match { + case REFERENCE(cls2) => + cls.tpe <:< cls2.tpe; + case ARRAY(_) => + cls == definitions.AllRefClass; + case _ => false; + } + + override def isReferenceType: Boolean = true; + + override def equals(other: Any): Boolean = other match { + case REFERENCE(cls2) => cls == cls2; + case _ => false; + } + } + + + case class ARRAY(val elem: TypeKind) extends TypeKind { + override def toString(): String = + "ARRAY[" + elem + "]"; + + override def isArrayType = true; + + /** + * Approximate `lub'. The common type of two references is + * always AnyRef. For 'real' least upper bound wrt to subclassing + * use method 'lub'. + */ + override def maxType(other: TypeKind): TypeKind = + other match { + case REFERENCE(_) => REFERENCE(definitions.AnyRefClass); + case ARRAY(elem2) => ARRAY(elem maxType elem2); + case _ => + abort("Uncomparbale type kinds: ARRAY with " + other); + } + + /** Checks subtyping relationship. */ + override def <:<(other: TypeKind): Boolean = + other match { + case ARRAY(elem2) => + elem <:< elem2; + case REFERENCE(sym) => + (sym == definitions.AnyRefClass || + sym == definitions.ObjectClass) // TODO: platform dependent! + + case _ => false; + } + + override def equals(other: Any): Boolean = other match { + case ARRAY(elem2) => elem == elem2; + case _ => false; + } + + } + + ////////////////// Conversions ////////////////////////////// + + + /** Return the TypeKind of the given type */ + def toTypeKind(t: Type): TypeKind = t match { + case ThisType(sym) => REFERENCE(sym); + + case SingleType(pre, sym) => + primitiveTypeMap get sym match { + case Some(k) => k; + case None => REFERENCE(sym); + } + + case ConstantType(value) => + toTypeKind(value.tpe); + + case TypeRef(_, sym, args) => + primitiveTypeMap get sym match { + case Some(k) => k; + case None => + if (sym == definitions.ArrayClass) + ARRAY(toTypeKind(args.head)) + else + REFERENCE(sym); + } + + case ClassInfoType(_, _, sym) => + primitiveTypeMap get sym match { + case Some(k) => k; + case None => + if (sym == definitions.ArrayClass) + abort("ClassInfoType to ArrayClass!"); + else + REFERENCE(sym); + } + + case _ => abort("Unknown type: " + t); + } + + /** A map from scala primitive Types to ICode TypeKinds */ + private var primitiveTypeMap: Map[Symbol, TypeKind] = null; + + /** Initialize the map from scala primitive types to ICode types */ + def initPrimitiveTypeMap = { + log("Initializing primitive map"); + primitiveTypeMap = new HashMap(); + primitiveTypeMap += definitions.UnitClass -> UNIT; + primitiveTypeMap += definitions.BooleanClass -> BOOL; + primitiveTypeMap += definitions.ByteClass -> BYTE; + primitiveTypeMap += definitions.ShortClass -> SHORT; + primitiveTypeMap += definitions.CharClass -> CHAR; + primitiveTypeMap += definitions.IntClass -> INT; + primitiveTypeMap += definitions.LongClass -> LONG; + primitiveTypeMap += definitions.FloatClass -> FLOAT; + primitiveTypeMap += definitions.DoubleClass -> DOUBLE; +// primitiveTypeMap += definitions.StringClass -> STRING; + } + +} diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala new file mode 100644 index 0000000000..1f0f8b887c --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala @@ -0,0 +1,96 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.tools.nsc.backend.icode.Primitives; + +trait TypeStacks: ICodes { + import opcodes._; + import global.{Symbol, Type, definitions}; + + /* This class simulates the type of the opperand + * stack of the ICode. + */ + type Rep = List[TypeKind]; + + class TypeStack { + var types: Rep = Nil; + + def this(stack: Rep) = { + this(); + this.types = stack; + } + + def this(that: TypeStack) = { + this(that.types); + } + + def length: Int = types.length; + + /** Push a type on the type stack. UNITs are ignored. */ + def push(t: TypeKind) = + if (t != UNIT) + types = t :: types; + + /** Removes the value on top of the stack, and returns it. It assumes + * the stack contains at least one element. + */ + def pop: TypeKind = { + val t = types.head; + types = types.tail; + t + } + + /** Return the topmost two values on the stack. It assumes the stack + * is large enough. Topmost element first. + */ + def pop2: Pair[TypeKind, TypeKind] = Pair(pop, pop); + + /** Return the topmost three values on the stack. It assumes the stack + * is large enough. Topmost element first. + */ + def pop3: Triple[TypeKind, TypeKind, TypeKind] = Triple(pop, pop, pop); + + /** + * A TypeStack aggress with another one if they have the same + * length and each type kind agrees position-wise. Two + * types agree if they are subtypes of one another. + */ + def agreesWith(other: TypeStack): Boolean = ( + (types.length == other.types.length) && + List.forall2(types, other.types) ((t1, t2) => t1 <:< t2 || t2 <:< t1) + ); + + def mergeWith(that: TypeStack): TypeStack = { + def merge(a: TypeStack, b: TypeStack): TypeStack = { + val lst = List.map2(a.types, b.types) ((k1, k2) => k1 match { + case REFERENCE(cls1) => + val REFERENCE(cls2) = k2; + lub(k1,k2); + case _ => k1; + }); + new TypeStack(lst) + } + + assert(this agreesWith that, + "Incompatible type stacks: " + this + ", " + that); + merge(this, that) + } + + /* This method returns a String representation of the stack */ + override def toString() = { + (types.foldLeft(new StringBuffer("")) ((buf, k) => buf.append(" ").append(k))).toString(); + } + + override def equals(other: Any): Boolean = ( + other.isInstanceOf[TypeStack] && + List.forall2(other.asInstanceOf[TypeStack].types, types)((a, b) => a == b) + ); + } + +} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala new file mode 100644 index 0000000000..50a86fdf27 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -0,0 +1,1054 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.jvm; + +import java.io.File; + +import scala.collection.mutable.{Map, HashMap}; +import scala.tools.nsc.symtab._; +import scala.tools.nsc.util.Position; + +import ch.epfl.lamp.fjbg._; + +/** + */ +abstract class GenJVM extends SubComponent { + import global._; + import icodes._; + import icodes.opcodes._; + + val phaseName = "jvm"; + + /** Create a new phase */ + override def newPhase(p: Phase) = new JvmPhase(p); + + /** JVM code generation phase + */ + class JvmPhase(prev: Phase) extends GlobalPhase(prev) { + def name = phaseName; + override def newFlags = phaseNewFlags; + + override def erasedTypes = true; + val codeGenerator = new BytecodeGenerator; + + override def run: Unit = + classes foreach codeGenerator.genClass; + + override def apply(unit: CompilationUnit): Unit = + abort("JVM works on icode classes, not on compilation units!"); + } + + /** + * Java bytecode generator. + * + */ + class BytecodeGenerator { + val MIN_SWITCH_DENSITY = 0.7; + val MODULE_INSTANCE_NAME = "MODULE$"; + val JAVA_LANG_STRINGBUFFER = "java.lang.StringBuffer"; + val JAVA_RMI_REMOTEEXCEPTION = "java.rmi.RemoteException"; + + val stringBufferType = new JObjectType(JAVA_LANG_STRINGBUFFER); + val toStringType = new JMethodType(JObjectType.JAVA_LANG_STRING, JType.EMPTY_ARRAY); + + // Scala attributes + val SerializableAttr = definitions.SerializableAttr; + val SerialVersionUID = definitions.getClass("scala.SerialVersionUID").tpe; + val CloneableAttr = definitions.getClass("scala.cloneable").tpe; + val TransientAtt = definitions.getClass("scala.transient").tpe; + val VolatileAttr = definitions.getClass("scala.volatile").tpe; + val RemoteAttr = definitions.getClass("scala.remote").tpe; + + val CloneableClass = definitions.getClass("java.lang.Cloneable"); + val RemoteInterface = definitions.getClass("java.rmi.Remote"); + + var clasz: IClass = _; + var method: IMethod = _; + var code: Code = _; + var jclass: JClass = _; + var jmethod: JMethod = _; + var jcode: JExtendedCode = _; + + val fjbgContext = new FJBGContext(); + + def emitClass(jclass: JClass, sym: Symbol): Unit = { + def addScalaAttr(sym: Symbol): Unit = currentRun.symData.get(sym) match { + case Some(pickle) => + val scalaAttr = fjbgContext.JOtherAttribute(jclass, + jclass, + nme.ScalaSignatureATTR.toString(), + pickle.bytes, + pickle.writeIndex); + jclass.addAttribute(scalaAttr); + currentRun.symData -= sym; + currentRun.symData -= sym.linkedSym; + //System.out.println("Generated ScalaSig Attr for " + sym);//debug + case _ => + log("Could not find pickle information for " + sym); + } + if (!jclass.getName().endsWith("$")) + addScalaAttr(if (isTopLevelModule(sym)) sym.sourceModule else sym); + val outfile = getFile(jclass, ".class"); + jclass.writeTo(outfile); + val file = scala.tools.util.AbstractFile.getFile(outfile); + informProgress("wrote " + outfile + " " + (if (file != null) "" + file.getFile() + " " + file.getFile().exists() else "no file")); + } + + var serialVUID: Option[Long] = None; + var remoteClass: Boolean = false; + + def genClass(c: IClass): Unit = { + if (settings.debug.value) + log("Generating class " + c.symbol + " flags: " + Flags.flagsToString(c.symbol.flags)); + clasz = c; + var parents = c.symbol.info.parents; + var ifaces = JClass.NO_INTERFACES; + val name = javaName(c.symbol); + serialVUID = None; + remoteClass = false; + + if (parents.isEmpty) + parents = definitions.ObjectClass.tpe :: parents; + + c.symbol.attributes foreach { a => a match { + case Pair(SerializableAttr, _) => + parents = parents ::: List(definitions.SerializableClass.tpe); + case Pair(CloneableAttr, _) => + parents = parents ::: List(CloneableClass.tpe); + case Pair(SerialVersionUID, value :: _) => + serialVUID = Some(value.longValue); + case Pair(RemoteAttr, _) => + parents = parents ::: List(RemoteInterface.tpe); + remoteClass = true; + case _ => (); + } + } + parents = parents.removeDuplicates; + + if (parents.length > 1 ) { + ifaces = new Array[String](parents.length - 1); + parents.drop(1).map((s) => javaName(s.symbol)).copyToArray(ifaces, 0); + () + } + + jclass = fjbgContext.JClass(javaFlags(c.symbol), + name, + javaName(parents(0).symbol), + ifaces, + c.cunit.source.toString()); + + if (isTopLevelModule(c.symbol) || serialVUID != None) { + if (isTopLevelModule(c.symbol)) + addModuleInstanceField; + addStaticInit(jclass); + + if (c.symbol.linkedClass != NoSymbol) + log("No mirror class for module with linked class: " + c.symbol.fullNameString); + else + dumpMirrorClass; + } + + clasz.fields foreach genField; + clasz.methods foreach genMethod; + + emitClass(jclass, c.symbol) + } + + def isTopLevelModule(sym: Symbol): Boolean = { + sym.isModuleClass && !sym.isImplClass && !sym.hasFlag(Flags.LIFTED) /* && !atPhase(currentRun.erasurePhase)(sym.isNestedClass) */ + } + + def genField(f: IField): Unit = { + if (settings.debug.value) + log("Adding field: " + f.symbol.fullNameString); + var attributes = 0; + + f.symbol.attributes foreach { a => a match { + case Pair(TransientAtt, _) => attributes = attributes | JAccessFlags.ACC_TRANSIENT; + case Pair(VolatileAttr, _) => attributes = attributes | JAccessFlags.ACC_VOLATILE; + case _ => (); + }} + jclass.addNewField(javaFlags(f.symbol) | attributes, + javaName(f.symbol), + javaType(toTypeKind(f.symbol.tpe))); + } + + def genMethod(m: IMethod): Unit = { + if (settings.debug.value) + log("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) + + " owner: " + m.symbol.owner); + method = m; + endPC.clear; + computeLocalVarsIndex(m); + + var resTpe = javaType(toTypeKind(m.symbol.tpe.resultType)); + if (m.symbol.isClassConstructor) + resTpe = JType.VOID; + + var flags = javaFlags(m.symbol); + if (jclass.isInterface()) + flags = flags | JAccessFlags.ACC_ABSTRACT; + + jmethod = jclass.addNewMethod(flags, + javaName(m.symbol), + resTpe, + javaTypes(m.params map (.kind)), + javaNames(m.params map (.sym))); + + if (m.symbol.hasFlag(Flags.BRIDGE)) + jmethod.addAttribute(fjbgContext.JOtherAttribute(jclass, jmethod, "Bridge", + new Array[Byte](0))); + if (remoteClass || (m.symbol.attributes contains Pair(RemoteAttr, Nil))) + if (jmethod.isPublic()) { + // (see http://java.sun.com/docs/books/vmspec/html/ClassFile.doc.html#3129) + // Exceptions_attribute { + // .. + // u2 number_of_exceptions; + // u2 exception_index_table[number_of_exceptions]; + // } + val cp = jclass.getConstantPool(); + val reInx = cp.addClass(JAVA_RMI_REMOTEEXCEPTION); + val contents = java.nio.ByteBuffer.allocate(4); // u2 + u2[1] + contents.putShort(1.asInstanceOf[Short]); + contents.putShort(reInx.asInstanceOf[Short]); + if (settings.debug.value) + log("adding 'Exceptions_attribute' " + contents + " for remote method " + method); + jmethod.addAttribute( + fjbgContext.JOtherAttribute(jclass, jmethod, "Exceptions", contents.array())); + } + + if (!jmethod.isAbstract()) { + for (val local <- m.locals; (! m.params.contains(local))) { + if (settings.debug.value) + log("add local var: " + local); + jmethod.addNewLocalVariable(javaType(local.kind), javaName(local.sym)); + } + + jcode = jmethod.getCode().asInstanceOf[JExtendedCode]; + genCode(m); + genLocalVariableTable; + } + } + + def addModuleInstanceField: Unit = { + import JAccessFlags._; + jclass.addNewField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, + MODULE_INSTANCE_NAME, + jclass.getType()); + } + + def addStaticInit(cls: JClass): Unit = { + import JAccessFlags._; + val clinitMethod = cls.addNewMethod(ACC_PUBLIC | ACC_STATIC, + "<clinit>", + JType.VOID, + JType.EMPTY_ARRAY, + new Array[String](0)); + val clinit = clinitMethod.getCode().asInstanceOf[JExtendedCode]; + if (isTopLevelModule(clasz.symbol)) { + clinit.emitNEW(cls.getName()); + clinit.emitDUP(); + clinit.emitINVOKESPECIAL(cls.getName(), + JMethod.INSTANCE_CONSTRUCTOR_NAME, + JMethodType.ARGLESS_VOID_FUNCTION); + } + + serialVUID match { + case Some(value) => + val fieldName = "serialVersionUID"; + jclass.addNewField(JAccessFlags.ACC_STATIC | JAccessFlags.ACC_PUBLIC, + fieldName, + JType.LONG); + clinit.emitPUSH(value); + clinit.emitPUTSTATIC(jclass.getName(), fieldName, JType.LONG); + case None => (); + } + + clinit.emitRETURN(); + } + + def dumpMirrorClass: Unit = { + import JAccessFlags._; + assert(clasz.symbol.isModuleClass); + if (settings.debug.value) + log("Dumping mirror class for object: " + clasz); + val moduleName = javaName(clasz.symbol); // + "$"; + val mirrorName = moduleName.substring(0, moduleName.length() - 1); + val mirrorClass = fjbgContext.JClass(ACC_SUPER | ACC_PUBLIC | ACC_FINAL, + mirrorName, + "java.lang.Object", + JClass.NO_INTERFACES, + clasz.cunit.source.toString()); + for (val m <- clasz.methods; !(m.symbol.hasFlag(Flags.PRIVATE)) && !m.symbol.isClassConstructor && !isStaticSymbol(m.symbol) ) { + val mirrorMethod = mirrorClass.addNewMethod(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, + javaName(m.symbol), + javaType(toTypeKind(m.symbol.tpe.resultType)), + javaTypes(m.params map (.kind)), + javaNames(m.params map (.sym))); + val mirrorCode = mirrorMethod.getCode().asInstanceOf[JExtendedCode]; + mirrorCode.emitGETSTATIC(moduleName, + MODULE_INSTANCE_NAME, + new JObjectType(moduleName)); + var i = 0; + var index = 0; + var argTypes = mirrorMethod.getArgumentTypes(); + while (i < argTypes.length) { + mirrorCode.emitLOAD(index, argTypes(i)); + index = index + argTypes(i).getSize(); + i = i + 1; + } + + mirrorCode.emitINVOKEVIRTUAL(moduleName, mirrorMethod.getName(), mirrorMethod.getType().asInstanceOf[JMethodType]); + mirrorCode.emitRETURN(mirrorMethod.getReturnType()); + } + emitClass(mirrorClass, clasz.symbol); + } + + + val linearizer = new NormalLinearizer(); + var linearization: List[BasicBlock] = Nil; + + var isModuleInitialized = false; + def genCode(m: IMethod): Unit = { + labels.clear; + isModuleInitialized = false; + + code = m.code; + linearization = linearizer.linearize(m); + makeLabels(linearization); + genBlocks(linearization); + + if (this.method.exh != Nil) + genExceptionHandlers; + } + + var nextBlock: BasicBlock = _; + + def genBlocks(l: List[BasicBlock]): Unit = l match { + case Nil => (); + case x :: Nil => nextBlock = null; genBlock(x); + case x :: y :: ys => nextBlock = y; genBlock(x); genBlocks(y :: ys); + } + + + /** Generate exception handlers for the current method. */ + def genExceptionHandlers: Unit = { + + def ranges(e: ExceptionHandler): List[Pair[Int, Int]] = { + var covered = e.covered; + var ranges: List[Pair[Int, Int]] = Nil; + var start = -1; + var end = -1; + + linearization foreach ((b) => { + if (! (covered contains b) ) { + if (start >= 0) { // we're inside a handler range + end = labels(b).getAnchor(); + ranges = Pair(start, end) :: ranges; + start = -1; + } + } else { + if (start >= 0) { // we're inside a handler range + end = endPC(b); + } else { + start = labels(b).getAnchor(); + end = endPC(b); + } + covered = covered remove b.==; + } + }); + + if (start >= 0) { + ranges = Pair(start, end) :: ranges; + } + + if (covered != Nil) + if (settings.debug.value) + log("Some covered blocks were not found in method: " + method + + " covered: " + covered + " not in " + linearization); + ranges + } + + this.method.exh foreach ((e) => { + ranges(e).sort({ (p1, p2) => p1._1 < p2._1 }) + .foreach ((p) => { + if (settings.debug.value) + log("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method + + " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls); + jcode.addExceptionHandler(p._1, p._2, + labels(e.startBlock).getAnchor(), + if (e.cls == NoSymbol) + null + else javaName(e.cls)) + }) + }); + } + + def genBlock(b: BasicBlock): Unit = { + labels(b).anchorToNext(); + + if (settings.debug.value) + log("Generating code for block: " + b + " at pc: " + labels(b).getAnchor()); + var lastMappedPC = 0; + var lastLineNr =0; + var crtPC = 0; + + b traverse ( instr => { + if (b.lastInstruction == instr) + endPC(b) = jcode.getPC(); + + instr match { + case THIS(clasz) => + jcode.emitALOAD_0(); + + case CONSTANT(const) => + const.tag match { + case UnitTag => (); + case BooleanTag => jcode.emitPUSH(const.booleanValue); + case ByteTag => jcode.emitPUSH(const.byteValue); + case ShortTag => jcode.emitPUSH(const.shortValue); + case CharTag => jcode.emitPUSH(const.charValue); + case IntTag => jcode.emitPUSH(const.intValue); + case LongTag => jcode.emitPUSH(const.longValue); + case FloatTag => jcode.emitPUSH(const.floatValue); + case DoubleTag => jcode.emitPUSH(const.doubleValue); + case StringTag => jcode.emitPUSH(const.stringValue); + case NullTag => jcode.emitACONST_NULL(); + case _ => abort("Unknown constant value: " + const); + } + + case LOAD_ARRAY_ITEM(kind) => + jcode.emitALOAD(javaType(kind)); + + case LOAD_LOCAL(local, isArg) => + if (isArg) + jcode.emitLOAD(indexOf(local), javaType(local.kind)); + else + jcode.emitLOAD(indexOf(local), javaType(local.kind)); + + case LOAD_FIELD(field, isStatic) => + var owner = javaName(field.owner); +// if (field.owner.hasFlag(Flags.MODULE)) owner = owner + "$"; + if (settings.debug.value) + log("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags)); + if (isStatic) + jcode.emitGETSTATIC(owner, + javaName(field), + javaType(field)); + else + jcode.emitGETFIELD(owner, + javaName(field), + javaType(field)); + + case LOAD_MODULE(module) => + assert(module.isModule || module.isModuleClass, "Expected module: " + module); + if (settings.debug.value) + log("genearting LOAD_MODULE for: " + module + " flags: " + + Flags.flagsToString(module.flags)); + jcode.emitGETSTATIC(javaName(module) /* + "$" */ , + MODULE_INSTANCE_NAME, + javaType(module)); + + case STORE_ARRAY_ITEM(kind) => + jcode.emitASTORE(javaType(kind)); + + case STORE_LOCAL(local, isArg) => + if (isArg) + jcode.emitSTORE(indexOf(local), javaType(local.kind)); + else + jcode.emitSTORE(indexOf(local), javaType(local.kind)); + + case STORE_FIELD(field, isStatic) => + val owner = javaName(field.owner); // + (if (field.owner.hasFlag(Flags.MODULE)) "$" else ""); + if (isStatic) + jcode.emitPUTSTATIC(owner, + javaName(field), + javaType(field)); + else + jcode.emitPUTFIELD(owner, + javaName(field), + javaType(field)); + + case CALL_PRIMITIVE(primitive) => + genPrimitive(primitive, instr.pos); + + // TODO: reference the type of the receiver instead of the + // method owner. + case CALL_METHOD(method, style) => + val owner = javaName(method.owner); // + (if (method.owner.isModuleClass) "$" else ""); + + style match { + case Dynamic => + if (method.owner.hasFlag(Flags.INTERFACE)) + jcode.emitINVOKEINTERFACE(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]) + else + jcode.emitINVOKEVIRTUAL(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]); + + case Static(instance) => + if (instance) { + jcode.emitINVOKESPECIAL(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]); + } else + jcode.emitINVOKESTATIC(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]); + + case SuperCall(_) => + jcode.emitINVOKESPECIAL(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]); + // we initialize the MODULE$ field immediately after the super ctor + if (isTopLevelModule(clasz.symbol) && !isModuleInitialized && + jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME && + javaName(method) == JMethod.INSTANCE_CONSTRUCTOR_NAME) { + isModuleInitialized = true; + jcode.emitALOAD_0(); + jcode.emitPUTSTATIC(jclass.getName(), + MODULE_INSTANCE_NAME, + jclass.getType()); + } + + + } + + case NEW(REFERENCE(cls)) => + val className = javaName(cls); + jcode.emitNEW(className); + + case CREATE_ARRAY(elem) => elem match { + case REFERENCE(_) | ARRAY(_) => + jcode.emitANEWARRAY(javaType(elem).asInstanceOf[JReferenceType]); + case _ => + jcode.emitNEWARRAY(javaType(elem)); + } + + case IS_INSTANCE(tpe) => + tpe match { + case REFERENCE(cls) => jcode.emitINSTANCEOF(new JObjectType(javaName(cls))); + case ARRAY(elem) => jcode.emitINSTANCEOF(new JArrayType(javaType(elem))); + case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe); + } + + case CHECK_CAST(tpe) => + tpe match { + case REFERENCE(cls) => jcode.emitCHECKCAST(new JObjectType(javaName(cls))); + case ARRAY(elem) => jcode.emitCHECKCAST(new JArrayType(javaType(elem))); + case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe); + } + + case SWITCH(tags, branches) => + val tagArray = new Array[Array[Int]](tags.length); + var caze = tags; + var i = 0; + while (i < tagArray.length) { + tagArray(i) = new Array[Int](caze.head.length); + caze.head.copyToArray(tagArray(i), 0); + i = i + 1; + caze = caze.tail; + } + val branchArray = new Array[JCode$Label](tagArray.length); + if (settings.debug.value) + log("Emitting SWITHCH:\ntags: " + tags + "\nbranches: " + branches); + jcode.emitSWITCH(tagArray, + (branches map labels dropRight 1).copyToArray(branchArray, 0), + labels(branches.last), + MIN_SWITCH_DENSITY); + + case JUMP(where) => + if (nextBlock != where) + jcode.emitGOTO_maybe_W(labels(where), false); // default to short jumps + + case CJUMP(success, failure, cond, kind) => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + if (nextBlock == success) { + jcode.emitIF_ICMP(conds(negate(cond)), labels(failure)); + // .. and fall through to success label + } else { + jcode.emitIF_ICMP(conds(cond), labels(success)); + if (nextBlock != failure) + jcode.emitGOTO_maybe_W(labels(failure), false); + } + + case REFERENCE(_) | ARRAY(_) => + if (nextBlock == success) { + jcode.emitIF_ACMP(conds(negate(cond)), labels(failure)); + // .. and fall through to success label + } else { + jcode.emitIF_ACMP(conds(cond), labels(success)); + if (nextBlock != failure) + jcode.emitGOTO_maybe_W(labels(failure), false); + } + + case _ => + kind match { + case LONG => jcode.emitLCMP(); + case FLOAT => jcode.emitFCMPG(); + case DOUBLE => jcode.emitDCMPG(); + } + if (nextBlock == success) { + jcode.emitIF(conds(negate(cond)), labels(failure)); + // .. and fall through to success label + } else { + jcode.emitIF(conds(cond), labels(success)); + if (nextBlock != failure) + jcode.emitGOTO_maybe_W(labels(failure), false); + } + } + + case CZJUMP(success, failure, cond, kind) => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + if (nextBlock == success) { + jcode.emitIF(conds(negate(cond)), labels(failure)); + } else { + jcode.emitIF(conds(cond), labels(success)); + if (nextBlock != failure) + jcode.emitGOTO_maybe_W(labels(failure), false); + } + + case REFERENCE(_) | ARRAY(_) => + if (nextBlock == success) { + jcode.emitIFNONNULL(labels(failure)); + } else { + jcode.emitIFNULL(labels(success)); + if (nextBlock != failure) + jcode.emitGOTO_maybe_W(labels(failure), false); + } + + case _ => + kind match { + case LONG => jcode.emitLCONST_0(); jcode.emitLCMP(); + case FLOAT => jcode.emitFCONST_0(); jcode.emitFCMPL(); + case DOUBLE => jcode.emitDCONST_0(); jcode.emitDCMPL(); + } + if (nextBlock == success) { + jcode.emitIF(conds(negate(cond)), labels(failure)); + } else { + jcode.emitIF(conds(cond), labels(success)); + if (nextBlock != failure) + jcode.emitGOTO_maybe_W(labels(failure), false); + } + } + + case RETURN(kind) => + jcode.emitRETURN(javaType(kind)); + + case THROW() => + jcode.emitATHROW(); + + case DROP(kind) => + kind match { + case LONG | DOUBLE => jcode.emitPOP2(); + case _ => jcode.emitPOP(); + } + + case DUP(kind) => + kind match { + case LONG | DOUBLE => jcode.emitDUP2(); + case _ => jcode.emitDUP(); + } + + + case MONITOR_ENTER() => + jcode.emitMONITORENTER(); + + case MONITOR_EXIT() => + jcode.emitMONITOREXIT(); + } + + crtPC = jcode.getPC(); + val crtLine = try { clasz.cunit.position(instr.pos).line; } catch { + case _: Error => lastLineNr; + } + //System.err.println("CRTLINE: " + instr.pos + " " + + // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine); + + if (crtPC > lastMappedPC) { + jcode.completeLineNumber(lastMappedPC, crtPC, crtLine); + lastMappedPC = crtPC; + lastLineNr = crtLine; + } + + }); + } + + + def genPrimitive(primitive: Primitive, pos: Int): Unit = { + primitive match { + case Negation(kind) => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitINEG(); + + case LONG => jcode.emitLNEG(); + case FLOAT => jcode.emitFNEG(); + case DOUBLE => jcode.emitDNEG(); + case _ => abort("Impossible to negate a " + kind); + } + + case Arithmetic(op, kind) => + op match { + case ADD => jcode.emitADD(javaType(kind)); + case SUB => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitISUB(); + case LONG => jcode.emitLSUB(); + case FLOAT => jcode.emitFSUB(); + case DOUBLE => jcode.emitDSUB(); + } + + case MUL => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitIMUL(); + case LONG => jcode.emitLMUL(); + case FLOAT => jcode.emitFMUL(); + case DOUBLE => jcode.emitDMUL(); + } + + case DIV => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitIDIV(); + case LONG => jcode.emitLDIV(); + case FLOAT => jcode.emitFDIV(); + case DOUBLE => jcode.emitDDIV(); + } + + case REM => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitIREM(); + case LONG => jcode.emitLREM(); + case FLOAT => jcode.emitFREM(); + case DOUBLE => jcode.emitDREM(); + } + + case NOT => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitPUSH(-1); + jcode.emitIXOR();; + case LONG => + jcode.emitPUSH(-1l); + jcode.emitLXOR();; + case _ => abort("Impossible to negate an " + kind); + } + + case _ => abort("Unknown arithmetic primitive " + primitive ); + } + + case Logical(op, kind) => Pair(op, kind) match { + case Pair(AND, LONG) => + jcode.emitLAND(); + case Pair(AND, INT) => + jcode.emitIAND(); + case Pair(AND, _) => + jcode.emitIAND(); + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + + case Pair(OR, LONG) => + jcode.emitLOR(); + case Pair(OR, INT) => + jcode.emitIOR(); + case Pair(OR, _) => + jcode.emitIOR(); + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + + case Pair(XOR, LONG) => + jcode.emitLXOR(); + case Pair(XOR, INT) => + jcode.emitIXOR(); + case Pair(XOR, _) => + jcode.emitIXOR(); + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + } + + case Shift(op, kind) => Pair(op, kind) match { + case Pair(LSL, LONG) => + jcode.emitLSHL(); + case Pair(LSL, INT) => + jcode.emitISHL(); + case Pair(LSL, _) => + jcode.emitISHL(); + jcode.emitT2T(javaType(INT), javaType(kind)); + + case Pair(ASR, LONG) => + jcode.emitLSHR(); + case Pair(ASR, INT) => + jcode.emitISHR(); + case Pair(ASR, _) => + jcode.emitISHR(); + jcode.emitT2T(javaType(INT), javaType(kind)); + + case Pair(LSR, LONG) => + jcode.emitLUSHR(); + case Pair(LSR, INT) => + jcode.emitIUSHR(); + case Pair(LSR, _) => + jcode.emitIUSHR(); + jcode.emitT2T(javaType(INT), javaType(kind)); + } + + case Conversion(src, dst) => + if (settings.debug.value) + log("Converting from: " + src + " to: " + dst); + if (dst == BOOL) { + Console.println("Illegal conversion at: " + clasz + + " at: " + method.sourceFile + ":" + Position.line(clasz.cunit.source, pos)); + } else + jcode.emitT2T(javaType(src), javaType(dst)); + + case ArrayLength(_) => + jcode.emitARRAYLENGTH(); + + case StartConcat => + jcode.emitNEW(JAVA_LANG_STRINGBUFFER); + jcode.emitDUP(); + jcode.emitINVOKESPECIAL(JAVA_LANG_STRINGBUFFER, + JMethod.INSTANCE_CONSTRUCTOR_NAME, + JMethodType.ARGLESS_VOID_FUNCTION); + + case StringConcat(el) => + val jtype = el match { + case REFERENCE(_) | ARRAY(_)=> JObjectType.JAVA_LANG_OBJECT; + case _ => javaType(el); + } + jcode.emitINVOKEVIRTUAL(JAVA_LANG_STRINGBUFFER, + "append", + new JMethodType(stringBufferType, + Predef.Array(jtype))); + case EndConcat => + jcode.emitINVOKEVIRTUAL(JAVA_LANG_STRINGBUFFER, + "toString", + toStringType); + + case _ => abort("Unimplemented primitive " + primitive); + } + } + + val endPC: HashMap[BasicBlock, Int] = new HashMap(); + val labels: HashMap[BasicBlock, JCode$Label] = new HashMap(); + val conds: HashMap[TestOp, Int] = new HashMap(); + + conds += EQ -> JExtendedCode.COND_EQ; + conds += NE -> JExtendedCode.COND_NE; + conds += LT -> JExtendedCode.COND_LT; + conds += GT -> JExtendedCode.COND_GT; + conds += LE -> JExtendedCode.COND_LE; + conds += GE -> JExtendedCode.COND_GE; + + val negate: HashMap[TestOp, TestOp] = new HashMap(); + + negate += EQ -> NE; + negate += NE -> EQ; + negate += LT -> GE; + negate += GT -> LE; + negate += LE -> GT; + negate += GE -> LT; + + def makeLabels(bs: List[BasicBlock]) = { + //labels.clear; + if (settings.debug.value) + log("Making labels for: " + method); + bs foreach (bb => labels += bb -> jcode.newLabel() ); + } + + + ////////////////////// local vars /////////////////////// + + def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe)); + + + def sizeOf(k: TypeKind): Int = k match { + case DOUBLE | LONG => 2; + case _ => 1; + } + + def indexOf(m: IMethod, sym: Symbol): Int = { + val Some(local) = m.lookupLocal(sym); + assert (local.index >= 0, + "Invalid index for: " + local); + local.index + } + + def indexOf(local: Local): Int = { + assert (local.index >= 0, + "Invalid index for: " + local); + local.index + } + + /** + * Compute the indexes of each local variable of the given + * method. + */ + def computeLocalVarsIndex(m: IMethod): Unit = { + var idx = 1; + if (isStaticSymbol(m.symbol)) + idx = 0; + + for (val l <- m.locals) { + if (settings.debug.value) + log("Index value for " + l + ": " + idx); + l.index = idx; + idx = idx + sizeOf(l.kind); + } + } + + ////////////////////// Utilities //////////////////////// + + /** Return the a name of this symbol that can be used on the Java + * platform. It removes spaces from names. + * + * Special handling: scala.All and scala.AllRef are 'erased' to + * scala.All$ and scala.AllRef$. This is needed because they are + * not real classes, and they mean 'abrupt termination upon evaluation + * of that expression' or 'null' respectively. This handling is + * done already in GenICode, but here we need to remove references + * from method signatures to these types, because such classes can + * not exist in the classpath: the type checker will be very confused. + */ + def javaName(sym: Symbol): String = { + val suffix = if (sym.hasFlag(Flags.MODULE) && !sym.isMethod && + !sym.isImplClass && + !sym.hasFlag(Flags.JAVA)) "$" else ""; + + if (sym == definitions.AllClass) + return "scala.All$" + else if (sym == definitions.AllRefClass) + return "scala.AllRef$"; + + (if (sym.isClass || (sym.isModule && !sym.isMethod)) + sym.fullNameString('/') + else + sym.simpleName.toString().trim()) + suffix; + } + + def javaNames(syms: List[Symbol]): Array[String] = { + val res = new Array[String](syms.length); + var i = 0; + syms foreach ( s => { res(i) = javaName(s); i = i + 1; } ); + res + } + + /** + * Return the Java modifiers for the given symbol. + * Java modifiers for classes: + * - public, abstract, final, strictfp (not used) + * for interfaces: + * - the same as for classes, without 'final' + * for fields: + * - public, protected, private + * - static, final + * for methods: + * - the same as for fields, plus: + * - abstract, synchronized (not used), strictfp (not used), native (not used) + */ + def javaFlags(sym: Symbol): Int = { + import JAccessFlags._; + + var jf: Int = 0; + val f = sym.flags; + jf = jf | (if (sym hasFlag Flags.PRIVATE) ACC_PRIVATE else ACC_PUBLIC); + jf = jf | (if ((sym hasFlag Flags.ABSTRACT) || + (sym hasFlag Flags.DEFERRED)) ACC_ABSTRACT else 0); + jf = jf | (if (sym hasFlag Flags.INTERFACE) ACC_INTERFACE else 0); + jf = jf | (if ((sym hasFlag Flags.FINAL) && !sym.enclClass.hasFlag(Flags.INTERFACE)) ACC_FINAL else 0); + jf = jf | (if (isStaticSymbol(sym)) ACC_STATIC else 0); + jf + } + + def isStaticSymbol(s: Symbol): Boolean = + s.hasFlag(Flags.STATIC) || s.hasFlag(Flags.STATICMEMBER) || s.owner.isImplClass; + + def javaType(t: TypeKind): JType = t match { + case UNIT => JType.VOID; + + case BOOL => JType.BOOLEAN; + case BYTE => JType.BYTE; + case SHORT => JType.SHORT; + case CHAR => JType.CHAR; + case INT => JType.INT; + case LONG => JType.LONG; + case FLOAT => JType.FLOAT; + case DOUBLE => JType.DOUBLE; + case REFERENCE(cls) => new JObjectType(javaName(cls)); + case ARRAY(elem) => new JArrayType(javaType(elem)); + } + + def javaType(s: Symbol): JType = + if (s.isMethod) + new JMethodType( + if (s.isClassConstructor) + JType.VOID else javaType(toTypeKind(s.tpe.resultType)), + javaTypes(s.tpe.paramTypes map toTypeKind)) + else + javaType(toTypeKind(s.tpe)); + + def javaTypes(ts: List[TypeKind]): Array[JType] = { + val res = new Array[JType](ts.length); + var i = 0; + ts foreach ( t => { res(i) = javaType(t); i = i + 1; } ); + res + } + +// def javaTypes(syms: List[Symbol]): Array[JType] = { +// val res = new Array[JType](syms.length); +// var i = 0; +// syms foreach ( s => { res(i) = javaType(toTypeKind(s.tpe)); i = i + 1; } ); +// res +// } + + def getFile(cls: JClass, suffix: String): String = { + val path = cls.getName().replace('.', File.separatorChar); + settings.outdir.value + File.separatorChar + path + suffix + } + + private def genLocalVariableTable: Unit = { + val vars: Array[JLocalVariable] = jmethod.getLocalVariables(); + + if (!settings.debuginfo.value || vars.length == 0) + return; + + val pool = jclass.getConstantPool(); + val pc = jcode.getPC(); + var anonCounter = 0; + + val lvTab = java.nio.ByteBuffer.allocate(2 + 10 * vars.length); + lvTab.putShort(vars.length.asInstanceOf[Short]); + for (val lv <- vars) { + val name = if (lv.getName() == null) { + anonCounter = anonCounter + 1; + "<anon" + anonCounter + ">" + } else lv.getName(); + + lvTab.putShort(0.asInstanceOf[Short]); + lvTab.putShort(pc.asInstanceOf[Short]); + lvTab.putShort(pool.addUtf8(name).asInstanceOf[Short]); + lvTab.putShort(pool.addUtf8(lv.getType().getSignature()).asInstanceOf[Short]); + lvTab.putShort(lv.getIndex().asInstanceOf[Short]); + } + val attr = + fjbgContext.JOtherAttribute(jclass, + jmethod, + "LocalVariableTable", + lvTab.array()); + jcode.addAttribute(attr); + } + } +} diff --git a/src/compiler/scala/tools/nsc/matching/AlgebraicMatchers.scala b/src/compiler/scala/tools/nsc/matching/AlgebraicMatchers.scala new file mode 100644 index 0000000000..4137b128eb --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/AlgebraicMatchers.scala @@ -0,0 +1,172 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ + +package scala.tools.nsc.matching; + +/** the pattern matcher, tweaked to work with regular patterns + * @author Burak Emir + */ +trait AlgebraicMatchers : TransMatcher { + + import global._; + + class AlgebraicMatcher extends PatternMatcher { + + import java.util.Vector ; + import java.util.Iterator ; + + var _m: PartialMatcher = _; + + override protected var delegateSequenceMatching = true; + override protected var optimize = false; + + /** constructs an algebraic pattern matcher from cases */ + def construct(m: PartialMatcher, cases: List[CaseDef]): Unit = + construct(m, cases, true); + + /** constructs an algebraic pattern matcher from cases */ + def construct(m: PartialMatcher, cases: List[CaseDef], doBinding: Boolean): Unit = { + this._m = m; + super.initialize( _m.selector, _m.owner, doBinding ); + + val it = cases.elements; + while (it.hasNext) { + val cdef = it.next; + /* + if(cdef != null) + Console.println("algebraic matcher: "+cdef.toString()); // DEBUG + else + scala.Predef.error("got CaseDef null in alg matcher!"); + */ + enter(cdef); + } + + //if (unit.global.log()) { + // unit.global.log("internal pattern matching structure"); + // print(); + // } + _m.tree = toTree(); + } + + + /** initializes this AlgebraicMatcher, see Matcher.initialize + void initialize() {} + */ + /* + def isStarApply(tree: Tree.Apply): Boolean = { + val params:Array[Symbol] = tree.fun.getType().valueParams(); + //System.err.println( tree.fun.type.resultType().symbol() ); + (tree.args.length == 1) + && (tree.getType().symbol().flags & Modifiers.CASE) != 0 + && params.length > 0 + && (params(params.length-1).flags & Modifiers.REPEATED) != 0; + } + */ + //////////// generator methods + + override def toTree(): Tree = { + + this.exit = currentOwner.newLabel(root.pos, "exitA") + .setInfo(new MethodType(List(resultType), resultType)); + + val result = exit.newValueParameter(root.pos, "resultA").setInfo( resultType ); + + Block( + List ( + ValDef(root.symbol, _m.selector) + ), + If( super.toTree(root.and), + LabelDef(exit, List(result), Ident(result)), + ThrowMatchError( _m.pos, resultType )) + ); + } + + protected override def toTree(node: PatternNode, selector: Tree): Tree = { + //System.err.println("AM.toTree called"+node); + if (node == null) + Literal(false); + else node match { + case SeqContainerPat( _, _ ) => + callSequenceMatcher( node, + selector ); + case _ => + super.toTree( node, selector ); + } + } + + /** collects all sequence patterns and returns the default + */ + def collectSeqPats(node1: PatternNode, seqPatNodes: Vector, bodies: Vector): PatternNode = { + var node = node1; + var defaultNode: PatternNode = null; + var exit = false; + do { + if( node == null ) + exit=true; //defaultNode = node; // break + else + node match { + case SeqContainerPat( _, _ ) => + seqPatNodes.add( node ); + bodies.add( super.toTree( node.and ) ); + node = node.or; + exit//defaultNode = node; // break; + + case _ => + defaultNode = node; + } + } while (!exit && (null == defaultNode)) ; + + defaultNode; + } + + def callSequenceMatcher(node: PatternNode, selector1: Tree): Tree = { + + //Console.println("calling sequent matcher for"+node); + /* ???????????????????????? necessary to test whether is a Seq? + gen.If(selector.pos, maybe And( Is(selector, seqpat.type()) )???) + */ + + // translate the _.and subtree of this SeqContainerPat + + val seqPatNodes = new Vector(); + val bodies = new Vector(); + + var defaultNode = collectSeqPats(node, seqPatNodes, bodies); + + val defaultCase = toTree(defaultNode, selector1); + + val wordRec = new SequenceMatcher(); + + val m = new PartialMatcher { + val owner = _m.owner; + val selector = selector1; + } + + var pats: scala.List[Tree] = Nil; + var body: scala.List[Tree] = Nil; + + val tmp = bodies.toArray(); + var j = 0; + val it = seqPatNodes.iterator(); + while (it.hasNext()) { + //pats(j) = it.next().asInstanceOf[SeqContainerPat].seqpat; + pats = it.next().asInstanceOf[SeqContainerPat].seqpat :: pats; + //body(j) = tmp(j).asInstanceOf[Tree]; + body = tmp(j).asInstanceOf[Tree] :: body; + j = j + 1; + } + //Tree defaultTree = toTree(node.or, selector); // cdef.body ; + + wordRec.construct(m, pats.reverse, body.reverse, defaultCase, doBinding); + + //_m.defs.addAll(m.defs); + + m.tree; + } + +} // class AlgebraicMatcher + +} diff --git a/src/compiler/scala/tools/nsc/matching/Autom2.scala b/src/compiler/scala/tools/nsc/matching/Autom2.scala new file mode 100644 index 0000000000..b99db700b4 --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/Autom2.scala @@ -0,0 +1,196 @@ +package scala.tools.nsc.matching ; + +//import java.util._ ; + +import scala.tools.nsc.util.Position; + +trait Autom2: TransMatcher { + + import global._; + + /** @param owner owner of the pattern matching expression + */ + abstract class Autom2Scala { + + val dfa: DetWordAutom; + val owner: Symbol; + + protected var optimize = true; + + final val FAIL = -1; + + /** symbol of the matcher DefDef or Label */ + var funSym:Symbol = _; + + /** symbol of the iterator ( scala.SequenceIterator ) */ + var iterSym: Symbol = _; + + /** symbol of the switching result ( scala.Int ) */ + var resultSym: Symbol = _; + + /** symbol of the state variable ( scala.Int ) */ + var stateSym: Symbol = _; + + /** symbol of variable holding current label */ + var curSym: Symbol = _; + + /** symbol of boolean variable that indicates we have not reached end of sequence */ + var hasnSym: Symbol = _; + + val am = new AlgebraicMatcher(); + + var pos: Int = Position.FIRSTPOS; + + val elementType: Type; + + def funRetType(): Type = { + funSym.tpe match { + case MethodType( _, retType )=> retType; + case _ => scala.Predef.error("quoi?"); + } + } + + def callFun(args: List[Tree]): Tree = Apply(Ident(funSym),args); + + // overridden in RightTracerInScala + def loadCurrentElem(body: Tree): Tree = { + Block( + List( + ValDef(this.hasnSym, _hasNext( _iter() ) ), + ValDef(this.curSym, If(Ident( hasnSym ), + _next( _iter() ), + EmptyTree)) + ), + body + ); + } + + /** bug ?? */ + def currentElem() = { Ident( curSym ) } + + def currentMatches(label: Label): Tree = { + label match { + case TreeLabel( pat ) => + _cur_match( pat ); + case SimpleLabel(lit: Literal) => + Equals( currentElem(), lit ); + case _ => // cannot happen + scala.Predef.error("expected either algebraic or simple label:"+label); + } + } + + // + // translation of automata to scala code + // + + + /** `[switchResult]' */ + def _swres(): Tree = { Ident( resultSym );} + + /** `<state>' param */ + def _state(): Tree = { Ident( stateSym ); } + + /** `<iterator>' param */ + def _iter(): Tree = { Ident( iterSym ); } + + /** simple optimization: if we are in a sink state, stop traversing sequence + */ + def stateWrap(i: Int): Tree = { + if( dfa.isSink( i )) + run_finished( i ); // state won't change! optimization + else + If( Negate( Ident( hasnSym )), + run_finished( i ), + code_state_NEW( i )); + } + + /** body of the matcherDefFun + */ + def code_body_NEW(): Tree = { + var cases: List[CaseDef] = Nil; + + //val tags = new Array[Int](dfa.nstates()); + //val bodies = new Array[Tree](dfa.nstates()); + var i = 0; while (i < dfa.nstates()) { + cases = CaseDef( Literal(i), stateWrap(i)) :: cases; + i = i + 1; + } + //if( optimize ) + loadCurrentElem( Match( _state(), cases )); + + /* + Tree res = code_error(); + for( int i = dfa.nstates-2; i>= 0; i-- ) + res = gen.If( Equals( _state(), gen.mkIntLit( pos, i )), + bodies[ i ] , + res ); + + return loadCurrentElem( res ); + */ + } + + /* calling the (AlgebraicMatcher)PatternMatcher here */ + def _cur_match(pat: Tree): Tree = { + val m: PartialMatcher = new PartialMatcher { + val owner = Autom2Scala.this.funSym; /* owner*/ + val selector = currentElem(); /* root */ + /* defs.boolean_TYPE() restype */ + }; + + am.construct( m, List ( + CaseDef( pat, Literal(true)), + CaseDef( Ident(nme.WILDCARD), Literal(false)) + ), + false); + am.toTree(); + } + + // @todo should be abstract + def code_delta( i:Int, label: Label): Tree = { + scala.Predef.error("should not happen"); + } + + /** some error happened which is due to bug in translation/automaton + */ + final def code_error(): Tree = { + ThrowMatchError( pos , funRetType() ); + } + + def code_fail(): Tree = { + Literal( FAIL ); //gen.mkIntLit(Position.FIRSTPOS, FAIL ); + } + + /** code for the return value of the automaton translation + */ + def run_finished(state: Int): Tree = { + if( dfa.isFinal( state )) + Literal( + dfa.finals.get( new Integer( state ) ).asInstanceOf[Integer] + .intValue() + ) + else + Literal( FAIL ); + } + + def code_state_NEW(i: Int): Tree = { + var stateBody = code_delta( i, DefaultLabel() ); + if( stateBody == null ) + stateBody = code_fail(); + val trans = dfa.deltaq( i ); + + val labs = dfa.labels().iterator(); + while(labs.hasNext()) { + val label = labs.next(); + val next = trans.get( label ).asInstanceOf[Integer]; + val action = code_delta( i, label.asInstanceOf[Label] ); + if( action != null ) { + /*assert*/ //stateBody != null : "stateBody is null"; + stateBody = If( currentMatches(label.asInstanceOf[Label] ), + action, + stateBody); + } + } + stateBody; + } + } +} diff --git a/src/compiler/scala/tools/nsc/matching/BerrySethis.scala b/src/compiler/scala/tools/nsc/matching/BerrySethis.scala new file mode 100644 index 0000000000..85638903b0 --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/BerrySethis.scala @@ -0,0 +1,800 @@ +package scala.tools.nsc.matching ; + +import java.util.{ HashSet, HashMap, TreeSet, TreeMap, Vector }; + +//import scala.compiler.printer.XMLAutomPrinter; + +trait BerrySethis: TransMatcher { + +import global._; +/** a Berry-Sethi style construction for nfas. + * this class plays is the "Builder" for the "Director" class WordRecognizer. + */ + +class BerrySethi { + + /* + def isStar(n: Name): boolean = { + TreeInfo.isNameOfStarPattern(n); + } + */ + /* + + String s = n.toString(); + return (s.indexOf("$") != -1) + &&(!s.startsWith("nest")); + } + */ + + var labels: HashSet = _; + + var pos: int = _; + // maps a literal pattern to an Integer ( the position ) + // is not *really* needed (postfix order determines position!) + var posMap: HashMap = _; // pos: Patterns -> Positions + // don't let this fool you, only labelAt is a real, surjective mapping + var labelAt: HashMap= _; // chi: Positions -> Obj + + var globalFirst: TreeSet= _; + + // results which hold all info for the NondetWordAutomaton + + var follow: HashMap= _; // follow: Positions -> Set[Positions] + + + // Unit test ? + def nullable(pat: Tree): Boolean = + //System.out.print("<nullable>"); + //DEBUG.print( pat ); + //System.out.println("</nullable>"); + pat match { + case Apply(_, _) => false; + case Sequence( trees ) => trees.isEmpty || (trees forall {nullable}); + case Star(t) => true; // ? new + case Bind(n, t) => nullable( t ); + case Alternative(choices) => choices exists {nullable} + case _ => false; + } + + + + /** returns true if a Sequence pattern matches the empty sequence + * @param pat the sequence pattern. + def nullableSequence(pat: Tree): Boolean = + pat match { + case Sequence(pats) => pats forall {nullable}; + } + } + */ + + /** returns true if a sequence of patterns (usually children of a + * sequence or subsequence node) is nullable. + * @param pats the sequence of patterns + def nullable(pats: Array[Tree]): Boolean = { + var result = true; + var i = 0; + while(i < pats.length && result){ + result = result && nullable( pats( i ) ); + i = i + 1 + } + return result; + } + */ + + /** computes first( alpha ) where alpha is a word regexp + */ + + def compFirst( pat:Tree ): TreeSet = { + //System.out.print("<compFirst>"); + //DEBUG.print( pat ); + //System.out.println("</compFirst>"); + pat match { + case Sequence( trees ) => + return compFirst( trees ); + case Typed(_,_) | Select(_,_) | Apply(_, _) => + val tmp = new TreeSet(); + tmp.add( posMap.get( pat ).asInstanceOf[Integer]); // singleton set + return tmp; + + case Literal( _ ) => + val tmp = new TreeSet(); + tmp.add( posMap.get( pat ).asInstanceOf[Integer]); // singleton set + return tmp; + //case Subsequence( Tree[] trees ) => + //return compFirst( trees ); + case Alternative( trees ) => + val tmp = new TreeSet(); + var i = 0; + while(i < trees.length) { + tmp.addAll( compFirst( trees( i ) )); + i = i + 1 + } + return tmp; + + case Bind(_, tree) => + return compFirst(tree); + + case Ident( name ) => + //if (name != Name.fromString("_")) + // error("unexpected pattern"); + val tmp = new TreeSet(); + tmp.add( posMap.get( pat ).asInstanceOf[Integer]); // singleton set + return tmp; + + case _ => + scala.Predef.error("unexpected pattern"); + } + } + + + + /** computes last( alpha ) where alpha is a word regexp + */ + def compLast(pat: Tree): TreeSet = { + //System.out.print("<last>"); + //DEBUG.print( pat ); + //System.out.println("</compLast>"); + pat match { + case Sequence( _ ) | Apply(_, _) => + val tmp = new TreeSet(); + tmp.add(posMap.get( pat ).asInstanceOf[Integer]); // singleton set + return tmp; + + case Literal( _ ) => + val tmp = new TreeSet(); + tmp.add( posMap.get( pat ).asInstanceOf[Integer]); // singleton set + return tmp; + + case Alternative( trees ) => + val tmp = new TreeSet(); + var i = 0; + while(i < trees.length) { + tmp.addAll( compLast( trees )); + i = i + 1 + } + return tmp; + + case Bind( _, tree ) => + return compLast( tree ); + + case _ => + scala.Predef.error("unexpected pattern"); + } + } + + + /** computes first(w) where w=alpha_1...alpha_n are successors of a + * sequence node + * @todo make tail recursive + */ + def compFirst( pats:scala.List[Tree] ): TreeSet = pats match { + case List() => new TreeSet(); + case x::xs => + val res = compFirst(x); + if(nullable(x)) + res.addAll(compFirst(xs)); + res + } + + // Unit test ? + + /** computes last(w) where w are successors of a sequence node + */ + def compLast(pats: scala.List[Tree]): TreeSet = pats match { + case List() => new TreeSet(); + + case _ => + /* + System.out.print("<last>"); + for( int k = 0; k<pats.length; k++) { + DEBUG.print( pats[k] ); + System.out.print(" "); + } + System.out.println(); + */ + + var i = pats.length - 1; + var tmp = pats( i ); + val result = compLast( tmp ); + i = i - 1; + while( nullable(tmp) && (i >= 0 )) { + tmp = pats( i ); + result.addAll( compLast( tmp )); + i = i + 1; + } + return result; + } + + // starts from the right-to-left + // precondition: pos is final + // pats are successor patterns of a Sequence node + // returns first-set (== follow set of initial state) + def compFollow(pats: scala.List[Tree]): TreeSet = { + var first:TreeSet = null; + this.recVars = new HashMap(); + var fol = new TreeSet(); + if( pats.length > 0 ) {//non-empty expr + var i = pats.length; + fol.add( new Integer( pos )); // don't modify pos ! + do { + i = i - 1; + first = compFollow1( fol, pats( i ) ); + if( nullable( pats( i ) )) + fol.addAll( first ); + else + fol = first; + //System.out.println("in compFollow: first"+first); + //System.out.println("in compFollow: fol"+fol); + + } while( i > 0 ); + } + this.follow.put(new Integer( 0 ), fol); + return fol; + } + + var recVars: HashMap = _; + + /** returns the first set of an expression, setting the follow set along + * the way + */ + def compFollow1( fol1:TreeSet , pat:Tree ): TreeSet = { + var fol = fol1; + //System.out.println("compFollow1("+fol+","+pat+")"); + pat match { + case Sequence( trees ) => + var first: TreeSet = null; + var i = trees.length; + if( i > 0 ) { // is nonempty + do { + i = i - 1; + first = compFollow1(fol, trees( i )); + if( nullable( trees( i ) )) + fol.addAll( first ); + else + fol = first; + } while( i > 0 ) ; + } + if( null == first ) first = new TreeSet(); + return first; + + case Alternative( choices ) => + val first = new TreeSet(); + var i = choices.length - 1; + while(i >= 0) { + first.addAll( compFollow1( fol, choices( i ) )); + i = i - 1; + } + return first; + + case Star(t) => + + val first = compFirst( t ); + fol.addAll(first); + compFollow1(fol,t); + + case Bind( n, t ) => // == can also be star + + val first = compFirst( t ); + //System.out.print("BIND" + first); + //recVars.put( pat.symbol, first ); + + // if( appearsRightmost( n, t )) + // follow = oldfollw + ownfirst + + //if( isStar( n ) ) + // fol.addAll( first ); // an iterated pattern + + // continue to compute follow sets with adjusted fol + return compFollow1( fol, t ); + + case Ident( n ) => + if ((pat.symbol != null ) + && pat.symbol.isPrimaryConstructor) { + // same as Apply + val pos = this.posMap.get( pat ).asInstanceOf[Integer]; + val tset = fol.clone().asInstanceOf[TreeSet]; + this.follow.put( pos, tset ); + val first = new TreeSet(); + first.add( pos ); + return first; + } + /* + if ( recVars.keySet().contains( pat.symbol )) { // grammar + val first = recVars.get( pat.symbol ).asInstanceOf[TreeSet]; + val follow = fol.clone().asInstanceOf[TreeSet]; + first.addAll( follow ); + //recVars.put//this.follow.put( pat.symbol, follow ); + return first; + } + */ + // --- --- only happens when called from BindingBerrySethi + // [... x ...] changed to [... x@_ ...] + + // non-generated, non-recursive variable should not appear, + // so this is a wildcard pattern _ + + val pos = this.posMap.get( pat ).asInstanceOf[Integer]; + val tset = fol.clone().asInstanceOf[TreeSet]; + this.follow.put( pos, tset ); + val first = new TreeSet(); + first.add( pos ); + //System.out.println("Ident("+n+",...) first:"+first); + //System.out.println("Ident("+n+",...) follow:"+tset); + return first; + + case Apply(_, _) | Literal( _ ) | Typed(_,_) | Select(_,_) => + val pos = this.posMap.get( pat ).asInstanceOf[Integer]; + val tset = fol.clone().asInstanceOf[TreeSet]; + this.follow.put( pos, tset ); + val first = new TreeSet(); + first.add( pos ); + return first; + + case _ => + scala.Predef.error("unexpected pattern: "+pat.getClass()); + } + } + + /** called at the leaves of the regexp + */ + def seenLabel( pat:Tree , i:Integer , label:Label ): Unit = { + this.posMap.put( pat, i ); + this.labelAt.put( i, label ); + if( label != DefaultLabel() ) { + /* + if( this.labels.contains( label ) ) { + switch(label) { + case TreeLabel(Apply(_, Tree[] args)) => + if( args.length > 0 ) { + unit.warning(pat.pos, "if this pattern in nondeterminism, it will not compile correctly"); + } + } + } + */ + this.labels.add( label ); + } + + } + + /** overriden in BindingBerrySethi + */ + def seenLabel( pat:Tree , label:Label ): Unit = { + seenLabel( pat, new Integer( {pos = pos + 1; pos} ), label ); + } + + /** returns "Sethi-length" of a pattern, creating the set of position + * along the way + */ + + var activeBinders:Vector = _; + + // todo: replace global variable pos with acc + def traverse( pat:Tree ): Unit = { + pat match { + + // (is tree automaton stuff, more than Berry-Sethi) + case Apply( _, _ ) | Typed( _, _ )| Select( _, _ ) => + val label = new TreeLabel( pat ); + seenLabel( pat, label ) ; + return ; + + case p @ Literal( _ ) => + val label = new SimpleLabel( p ); + seenLabel( pat, label ) ; + + return ; + + case Sequence( trees ) => + var i = 0; + while(i < trees.length) { + traverse( trees( i ) ); + i = i + 1 + } + return ; + + case Alternative( trees ) => + var i = 0; + while(i < trees.length) { + traverse( trees( i ) ); + i = i + 1 + } + return ; + + case Bind( name, body) => + //recVars.put( pat.symbol, java.lang.Boolean.TRUE ); + //if( !isStar( name ) ) { + activeBinders.add( pat.symbol ); + traverse( body ); + activeBinders.remove( pat.symbol ); + //} + //else + // + case Star(body) => traverse( body ); + + case Ident(name) => + if ((pat.symbol != null ) + && pat.symbol.isPrimaryConstructor) { + // same as Apply + val label = new TreeLabel( pat ); + seenLabel( pat, label ) ; + + return ; + } + + scala.Predef.error("should not get here"); // removed idents? + //if( null != recVars.get( pat.symbol ) ) { + // return ; + //} + // _ and variable x ( == x @ _ ) + val label = DefaultLabel(); + seenLabel( pat, label ); + + return ; + + } + } + + + var finals: TreeMap = _; // final states + + //TreeSet initialsRev; // final states + + var deltaq:Array[HashMap] = _; // delta + + + + var defaultq: Array[Vector] = _; // default transitions + + + //HashMap deltaqRev[]; // delta of Rev + //Vector defaultqRev[]; // default transitions of Rev + + + def makeTransition(srcI:Integer, destI:Integer, label: Label): Unit = { + var src = srcI.intValue() ; + var dest = destI.intValue() ; + var arrows: Vector = null; //, revArrows; + //Label revLabel = new Pair( srcI, label ); + label match { + case DefaultLabel() => + arrows = defaultq( src ); + //revArrows = defaultqRev[ dest ]; + case _ => + arrows = deltaq( src ).get( label ).asInstanceOf[Vector]; + if( arrows == null ) + deltaq( src ).put( label, + {arrows = new Vector(); arrows} ); + /* + revArrows = (Vector) deltaqRev[ dest ].get( revLabel ); + if( revArrows == null ) + deltaqRev[ dest ].put( revLabel, + revArrows = new Vector() ); + */ + } + arrows.add( destI ); + //revArrows.add( srcI ); + } + + + var initials: TreeSet = _; + //NondetWordAutom revNfa ; + + def initialize( subexpr:List[Tree] ): Unit = { + this.posMap = new HashMap(); + this.labelAt = new HashMap(); + + + this.follow = new HashMap(); + this.labels = new HashSet(); + this.recVars = new HashMap(); + this.pos = 0; + // determine "Sethi-length" of the regexp + activeBinders = new Vector(); + val it = subexpr.elements; + while(it.hasNext ) + traverse( it.next ); + + + this.initials = new TreeSet(); + initials.add( new Integer( 0 )); + + } + + def initializeAutom(): Unit = { + + finals = new TreeMap(); // final states + deltaq = new Array[HashMap]( pos ); // delta + defaultq = new Array[Vector]( pos ); // default transitions + + var j = 0; + while(j < pos) { + deltaq( j ) = new HashMap(); + defaultq( j ) = new Vector(); + j = j + 1; + } + } + + def collectTransitions(): Unit = { // make transitions + var j = 0; + while(j < pos) { + val q = new Integer( j ); + + //System.out.print( "--q="+q ); + //System.out.println(" labelAt:"+labelAt.get( q )); + + val fol = this.follow.get( q ).asInstanceOf[TreeSet]; + //assert fol != null; + val it = fol.iterator(); + while(it.hasNext()) { + val p = it.next().asInstanceOf[Integer]; + //System.out.println( "-- -- p="+p ); + if( p.intValue() == pos ) { + finals.put( q, finalTag ); + } else { + makeTransition( new Integer(j), p, + labelAt.get( p ).asInstanceOf[Label]); + } + } + j = j + 1 + } + } + + var finalTag: Integer = _; + + def automatonFrom(pat: Tree , finalTag: Integer): NondetWordAutom = { + + this.finalTag = finalTag; + + //System.out.println( "enter automatonFrom("+pat+","+finalTag+")"); // UNIT TEST + //System.out.println( pat ); + //System.out.println( nullableSequence( pat )); // UNIT TEST + pat match { + case Sequence( subexpr ) => + initialize( subexpr ); + + + // (1) compute first + + //globalFirst = compFirst( subexpr ); + //System.out.println(globalFirst); + + // (2) compute follow; + pos = pos + 1; + //Set ignore = compFollow( subexpr ); + //System.out.println(ignore); + //System.exit(0); + //assert (ignore.equals( globalFirst )); + + globalFirst = compFollow( subexpr ); + + //System.out.print("someFirst:");debugPrint(someFirst); + + // construct the automaton's explicit representation + + initializeAutom(); + + + // defaultqRev = new Vector[pos]; // default transitions + + collectTransitions(); + + if (subexpr forall {nullable}) // initial state is final + finals.put(new Integer(0), finalTag); + + //TreeSet initials = new TreeSet(); + //initials.add(new Integer(0)); + + val result = + new NondetWordAutom(pos, // = nstates + labels, + initials, + finals, + deltaq, + defaultq, + null/*(Object) qbinders*/); + + /* + System.out.println("inBerrySethi"); + XMLAutomPrinter pr = new XMLAutomPrinter( System.out ); + pr.begin(); + pr.print(result); + pr.print(revNfa); + pr.end(); + System.out.println("initialsRev = "+initialsRev); + System.out.println("outBerrySethi"); + */ + //System.exit(0); + //result.print(); + return result; + } + + scala.Predef.error("expected a sequence pattern"); + } + + def print1(): Unit = { + Console.println("after sethi-style processing"); + Console.println("#positions:" + pos); + Console.println("posMap:"); + + var it = this.posMap.keySet().iterator(); + while(it.hasNext()) { + val t = it.next().asInstanceOf[Tree]; + t match { + case Literal( _ ) => + Console.print( "(" + t.toString() + " -> "); + val s2 = (posMap.get(t).asInstanceOf[Integer]).toString(); + Console.print( s2 +") "); + } + } + Console.println("\nfollow: "); + var j = 1; + while(j < pos ) { + val fol = this.follow.get(new Integer(j)).asInstanceOf[TreeSet]; + Console.print("("+j+" -> "+fol.toString()+") "); + //debugPrint( fol ); + Console.println; + j = j + 1; + } + + } +} // class BerrySethi + +//import scala.compiler.printer.XMLTreePrinter ; +//import scala.compiler.printer.XMLAutomPrinter ; + +/** a Berry-Sethi style construction for nfas. + * this class plays is the "Builder" for the "Director" class + * WordRecognizer. + */ + +class BindingBerrySethi extends BerrySethi { + + // variables + + var deltaqRev : Array[HashMap] = _; // delta of Rev + var defaultqRev: Array[Vector] = _; // default transitions of Rev + var qbinders : Array[Vector] = _; // transitions <-> variables + var revnfa : NondetWordAutom = _ ; + var varAt : HashMap = _; // chi: Positions -> Vars (Symbol) + + override def makeTransition(srcI: Integer, destI: Integer, label: Label): Unit = { + val src = srcI.intValue() ; + val dest = destI.intValue() ; + var arrows: Vector = null; + var revArrows: Vector = null; + val revLabel = new LPair(srcI, label); + label match { + case DefaultLabel() => + arrows = defaultq(src); + revArrows = defaultqRev(dest); + + case _ => + arrows = deltaq(src).get(label).asInstanceOf[Vector]; + if (arrows == null) + deltaq(src).put(label, + {arrows = new Vector(); arrows} ); + revArrows = deltaqRev(dest).get(revLabel).asInstanceOf[Vector]; + if (revArrows == null) + deltaqRev(dest).put(revLabel, {revArrows = new Vector(); revArrows} ); + } + arrows.add(destI); + revArrows.add(srcI); + } + + override def seenLabel(pat: Tree, label: Label): Unit = { + var i = new Integer({pos = pos + 1; pos} ); + seenLabel( pat, i, label ); + pat match { + case Apply(_, _) | Literal( _ ) | Select(_, _) | Typed(_,_) => + this.varAt.put( i, activeBinders.clone() ); // below @ ? + + case Ident( name ) => + //assert ( pat.symbol() == Global.instance.definitions.PATTERN_WILDCARD )||( name.toString().indexOf("$") > -1 ) : "found variable label "+name; + + val binders = activeBinders.clone().asInstanceOf[Vector]; + /* + if( pat.symbol() != Global.instance.definitions.PATTERN_WILDCARD) { + binders.add( pat.symbol() ); + } + */ + this.varAt.put( i, binders ); + } + } + + override def initialize( pats:List[Tree] ): Unit = { + this.varAt = new HashMap(); // Xperiment + super.initialize( pats ); + } + + override def initializeAutom(): Unit = { + super.initializeAutom(); + deltaqRev = new Array[HashMap](pos); // deltaRev + defaultqRev = new Array[Vector](pos); // default transitions + qbinders = new Array[Vector](pos); // transitions <-> variables + + var j = 0; + while (j < pos) { + deltaqRev(j) = new HashMap(); + defaultqRev(j) = new Vector(); + qbinders(j) = varAt.get(new Integer(j)).asInstanceOf[Vector]; + j = j + 1; + } + varAt.clear(); // clean up + } + + + override def automatonFrom(pat: Tree, finalTag: Integer): NondetWordAutom = { + this.finalTag = finalTag ; + //System.out.println("enter automatonFrom("+ pat +")"); + pat match { + case Sequence(subexpr) => + + initialize(subexpr); + + // (1) compute first + follow; + pos = pos + 1; + + globalFirst = compFollow( subexpr ); + + + + initializeAutom(); // explicit representation + + collectTransitions(); + + val result = + new NondetWordAutom(pos, // = nstates + labels, + initials, + finals, + deltaq, + defaultq, + qbinders); + + result.leftTrans = true; + + val revInitials = new TreeSet(finals.keySet()); + /* + pos++; // adding a state + HashSet deltaqRev2[] = new HashSet[ deltaqRev.length + 1]; + HashSet defaultqRev2[] = new HashSet[ deltaqRev.length + 1]; + HashSet qbinders[] = new HashSet[ deltaqRev.length + 1]; + for (Iterator it = finals.keySet().iterator(); it.hasNext(); ) { + + } + */ + val revFinals = new TreeMap(); + var it = initials.iterator(); + while(it.hasNext()) { + revFinals.put(it.next(), finalTag); + } + revnfa = new NondetWordAutom(pos, + labels, + revInitials, + revFinals, + deltaqRev, + defaultqRev, + qbinders); + + revnfa.rightTrans = true; + + /* + System.out.println("inBerrySethi"); + XMLAutomPrinter pr = new XMLAutomPrinter(System.out); + pr.begin(); + pr.print(result); + pr.print(revnfa); + pr.end(); + System.out.println("initialsRev = " + initialsRev); + System.out.println("outBerrySethi"); + */ + //System.exit(0); + return result; //print(); + } + } + +} // class BindingBerrySethi + + + +} diff --git a/src/compiler/scala/tools/nsc/matching/CodeFactory.scala b/src/compiler/scala/tools/nsc/matching/CodeFactory.scala new file mode 100644 index 0000000000..1d6882a0fa --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/CodeFactory.scala @@ -0,0 +1,260 @@ +/* +** $Id$ +*/ +package scala.tools.nsc.matching ; + +import scala.tools.nsc.util.Position; + +[_trait_] abstract class CodeFactory: TransMatcher { + + import global._ ; + + import definitions._; // standard classes and methods + import typer.typed; // methods to type trees + import posAssigner.atPos; // for filling in tree positions + + + /** returns `List[ Tuple2[ scala.Int, <elemType> ] ]' */ + def SeqTraceType( elemType: Type ): Type = { + appliedType(definitions.ListClass.typeConstructor, + List(pairType(definitions.IntClass.info, + elemType))) + } + + + + def pairType(left: Type, right: Type) = { + appliedType( definitions.TupleClass(2).typeConstructor, + List(left,right)) + } + + /** returns `Iterator[ elemType ]' */ + def _seqIterType( elemType: Type ): Type = { + appliedType( definitions.IteratorClass.typeConstructor, + List(elemType)) + } + + /** returns A for T <: Sequence[ A ] + */ + def getElemType_Sequence(tpe: Type): Type = { + //System.err.println("getElemType_Sequence("+tpe.widen()+")"); + val tpe1 = tpe.widen.baseType( definitions.SeqClass ); + + if( tpe1 == NoType ) + Predef.error("arg "+tpe+" not subtype of Seq[ A ]"); + + return tpe1.typeArgs( 0 ); + } + + + // --------- these are new + + /** a faked switch statement + */ + def Switch(condition: Array[Tree], body: Array[Tree], defaultBody: Tree): Tree = { + //assert condition != null:"cond is null"; + //assert body != null:"body is null"; + //assert defaultBody != null:"defaultBody is null"; + var result = defaultBody; + + var i = condition.length-1; + while (i >= 0) { + result = If(condition(i), body(i), result); + i = i - 1 + } + + return result ; + } + + /** returns code `<seqObj>.elements' */ + def newIterator( seqObj:Tree ): Tree = + Apply(Select(seqObj, newTermName("elements")), List()); + + + /** `it.next()' */ + def _next(iter: Tree) = + Apply(Select(iter, definitions.Iterator_next), List()); + + + /** `it.hasNext()' */ + def _hasNext(iter: Tree) = + Apply(Select(iter, definitions.Iterator_hasNext), List()); + + + /** `!it.hasCur()' */ + def _not_hasNext( iter:Tree ) = + Apply(Select(_hasNext(iter), definitions.Boolean_not), List()); + + + /** `trace.isEmpty' */ + def isEmpty( iter: Tree ): Tree = + Apply(Select(iter, definitions.List_isEmpty), List()); + + + /** `arg.head' */ + def SeqList_head( arg: Tree ) = + Apply(Select(arg, definitions.List_head), List()); + + + def Negate(tree: Tree) = tree match { + case Literal(Constant(value:Boolean))=> + Literal(Constant(!value)) + case _ => + Apply(Select(tree, definitions.Boolean_not), List()); + } + + /*protected*/ def And(left: Tree, right: Tree): Tree = left match { + case Literal(Constant(value:Boolean)) => + if(value) right else left; + case _ => + right match { + case Literal(Constant(true)) => + left; + case _ => + Apply(Select(left, definitions.Boolean_and), List(right)); + } + } + + /*protected*/ def Or(left: Tree, right: Tree): Tree = { + left match { +/* + case If(cond: Tree, thenp: Tree, Literal(Constant(false))) => // little opt, frequent special case + If(cond, thenp, right) +*/ + case Literal(Constant(value: Boolean))=> + if(value) left else right; + case _ => + right match { + case Literal(Constant(false)) => + left; + case _ => + Apply(Select(left, definitions.Boolean_or), List(right)); + } + } + } + + // used by Equals + private def getCoerceToInt(left: Type): Symbol = { + val sym = left.nonPrivateMember( nme.coerce ); + //assert sym != Symbol.NONE : Debug.show(left); + + sym.alternatives.find { + x => x.info match { + case MethodType(vparams, restpe) => + vparams.length == 0 && isSameType(restpe,definitions.IntClass.info) + } + }.get + } + + // used by Equals +/* + private def getEqEq(left: Type, right: Type): Symbol = { + //Console.println("getEqeq of left == "+left); + val sym = left.nonPrivateMember( nme.EQEQ ); + + + //if (sym == NoSymbol) + // error("no eqeq for "+left); + // : Debug.show(left) + "::" + Debug.show(left.members()); + + var fun: Symbol = null; + var ftype:Type = null; // faster than `definitions.AnyClass.tpe' + sym.alternatives.foreach { + x => + //Console.println("getEqEq: "+x); + val vparams = x.info.paramTypes; + //Console.println("vparams.length == "+vparams.length); + + if (vparams.length == 1) { + val vptype = vparams(0); + //Console.println("vptype == "+vptype); + //Console.println(" ftype == "+ftype); + //Console.println(" cond1 == "+isSubType(right, vptype)); + //Console.println(" cond2("+vptype+","+ftype+") == "+(ftype == null || isSubType(vptype, ftype))); + //Console.println("vptype.getClass "+vptype.getClass()); + if (isSubType(right, vptype) && (ftype == null || isSubType(vptype, ftype)) ) { + fun = x; + ftype = vptype; + //Console.println("fun now: "+fun+" ftype now "+ftype); + } + } + } + //if (fun == null) scala.Predef.error("couldn't find eqeq for left"+left); + fun; + } +*/ + def Equals(left: Tree , right: Tree ): Tree = Apply(Select(left, nme.EQEQ), List(right)); +/* { var left = left1; + var right = right1; +*/ +/* + //Console.println("CodeFactory:: left.tpe =" + left.tpe + " right.tpe "+right.tpe+")"); + val ltype = left.tpe.widen; + var rtype = right.tpe.widen; + if (isSameType(ltype, rtype) + && (isSameType(ltype, definitions.CharClass.info) + || isSameType(ltype,definitions.ByteClass.info) + || isSameType(ltype,definitions.ShortClass.info))) + { + //Console.println("getcoerce"+getCoerceToInt(rtype)); + //Console.println("getcoerce.tpe"+getCoerceToInt(rtype).tpe); + right = Apply(Select(right, getCoerceToInt(rtype)), List()); + rtype = definitions.IntClass.info; + } + val eqsym = getEqEq(ltype, rtype); +*/ + //Console.println("eqsym "+eqsym); + //Console.println("eqsym.tpe "+eqsym.tpe); +// Apply(Select(left1, nme.EQEQ/*eqsym*/), List(right1)); +// } + + def ThrowMatchError(pos: Int, tpe: Type ) = + atPos(pos) { + Throw( + New( + TypeTree(definitions.MatchErrorClass.tpe), + List(List( + Literal(cunit.toString()), + Literal(Position.line(cunit.source, pos)))))) + } + +/* + Apply( + TypeApply( + gen.mkRef(definitions.MatchError_fail), + List(TypeTree(tpe)) + ), + List( + Literal(cunit.toString()), + Literal(Position.line(cunit.source, pos)) + ) + ); +*/ + + /* // ?! + def ThrowMatchError(pos:int , tree:Tree ) = + Apply( + gen.mkRef(definitions.MatchError_report), + List( + Literal(cunit.toString()), + Literal(Position.line(cunit.source, pos)), + tree + ) + ); + */ + +// def Error(pos: Int) = +// ThrowMatchError(pos); + + + /* + def newPair(left: Tree, right: Tree) = + New( + Apply( + gen.mkRef(definitions.TupleClass(2)), + List(left,right) + ) + ); + */ +} + diff --git a/src/compiler/scala/tools/nsc/matching/DetWordAutoms.scala b/src/compiler/scala/tools/nsc/matching/DetWordAutoms.scala new file mode 100644 index 0000000000..8e4d2943be --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/DetWordAutoms.scala @@ -0,0 +1,884 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ +package scala.tools.nsc.matching ; + +import java.util._ ; + +trait DetWordAutoms: TransMatcher { + +import global._; +class DetWordAutom { + + /** determinization -- standard algorithm considering only + * reachable states + */ + def this(nfa: NondetWordAutom) = { + this(); + //Console.println("DWA:this(.)"); + //Console.println(nfa.nstates); + //nfa.print(); + determinize( nfa ); + //Console.println(_nstates); + } + + //final static Integer FINTAG = new Integer(0); + + /** number of states */ + var _nstates:int =0; + + /** the 'alphabet' */ + var _labels:HashSet = _; + + /** the set of final states, here as a TreeMap */ + /*protected*/ var finals:TreeMap = _; + + /** dfa: HashMap trans: Object -> Integer + * nfa: HashMap trans: Object -> Vector [ Integer ] + * + * nfa: Integer ->(Object -> Vector [ Int ]) + * [q] |->( a |-> { q' | (q,a,q') in \deltaright } ) + * + * dfa: Integer ->(Object -> Int) + * [q] |->( a |-> q' | \deltaright(q,a) = q' } ) + */ + + var _deltaq: Array[HashMap] = _; + + var _defaultq: Array[Integer] = _; // this gives the default transitions + + //protected HashMap deltaq[]; + + // --- accessor methods + + /** returns number of states + */ + def nstates(): Int = _nstates; + + /** returns the labels + */ + def labels(): HashSet = _labels; + + /** returns the transitions + */ + def deltaq( state:int ): HashMap = _deltaq( state ); + + /** returns the transitions + */ + def deltaq( state:Integer ): HashMap = _deltaq( state.intValue() ); + + /** for a set of nfa states (that must exist), returns its transitions + */ + def deltaq(nset: TreeSet): HashMap = + deltaq( indexMap.get( nset ).asInstanceOf[Integer] ); + + /** for a set of nfa states (that must exist), returns its transitions + */ + def defaultq( nset:TreeSet ): Integer = + defaultq( indexMap.get( nset ).asInstanceOf[Integer] ); + + /** returns the transitions + */ + def defaultq( state: int ): Integer = + _defaultq( state ); + + /** returns the transitions + */ + def defaultq( state: Integer ): Integer = + _defaultq( state.intValue() ); + + /** returns true if the state is final + */ + def isFinal(state: int): boolean = + ((finals != null) + && (finals.get( new Integer( state )) != null)); + + /** returns true if the state is final + */ + def isFinal(state: Integer): boolean = { + return ((finals != null) && finals.containsKey( state )); + } + + /** returns true if the state is final + */ + def finalTag( state:Integer ): Integer = { + return finals.get( state ).asInstanceOf[Integer]; + } + + + def finalTag( state: int ): Integer = { + return finals.get( new Integer (state )).asInstanceOf[Integer]; + } + + /** returns true if the set of states contains at least one final state + */ + def containsFinal( Q: TreeSet ): boolean = { + val it = Q.iterator(); + while(it.hasNext()) { + if( isFinal(it.next().asInstanceOf[Integer])) + return true; + } + return false; + } + + + /** returns true if there are no finite states + */ + def isEmpty(): boolean = { + return finals.isEmpty(); + } + + // END stuff from FiniteAutom + + final val FIRST: int = 0; + final val LAST: int = FIRST + 1; + + //static final int WHICH_LONGEST_MATCH = FIRST ; + final val WHICH_LONGEST_MATCH:int = LAST ; + + // inherited from FiniteAutom: + + // int nstates; // number of states + // HashSet labels;// the alphabet + // TreeMap finals; + + // HashMap deltaq[]; + //Integer defaultq[]; + + + // TEMPORARY VAR used only during determinization and debug printing + // Q -> (Label -> Q ) + var delta/*Map*/ : HashMap = _; + // Q -> Integer; + var indexMap: HashMap = _; + + // Integer -> Q + var invIndexMap: HashMap = _; + + // only not null if this is a right-transducer + var qbinders: Array[Vector] = _; + + final val NODEFAULT: Integer = new Integer( -1 ); + + def isSink( i:int ): boolean = { + return ( _deltaq( i ).keySet().isEmpty() + && (_defaultq != null ) + && (_defaultq( i ).intValue() == i) ); + } + + def hasDefault( i:int ): boolean = { + return _defaultq( i ) != NODEFAULT; + } + + def determinize( nfa: NondetWordAutom ): Unit = { + //Console.println("DetWordAutom:determinize"); + //System.out.println("nfa:");nfa.print(); + var states:TreeSet = null; // temp: Set[Set[Integer]] + var deftrans:HashMap = null; // Set[Integer] -> Int + + var trans: HashMap = null; // always points to a mapping ( Label -> Q ) + var ix = 0; // state index + + this._labels = nfa.labels; + ////System.out.println("Labels: "+labels); + this.delta/*Map*/ = new HashMap(); + //this.dead = -1; + + states = new TreeSet( new StateSetComparator() ); + deftrans = new HashMap(); + // temporarily: Map[Set[Integer]] later: Map[Integer] + this.finals = new TreeMap( new StateSetComparator() ); + this.invIndexMap = new HashMap(); + this.indexMap = new HashMap(); + + // new initial state (singleton set { q0 } by construction) + val q0 = new TreeSet(); + q0.addAll( nfa.initials ); /*new Integer( 0 )); */ + states.add( q0 ); + + val empty = new TreeSet(); + deftrans.put( q0, empty ); + states.add( empty ); + deftrans.put( empty, empty ); + + val rest = new Stack(); + if( nfa.isFinal( 0 ) ) + this.finals.put( q0, nfa.finalTag( 0 ) ); + + //Console.println("...beginning"); + + rest.push( empty ); + rest.push( q0 ); + //Console.println("...beginning 2" ); + while( !rest.empty() ) { + //Console.println("...beginning 3" ); + val P1 = rest.pop().asInstanceOf[TreeSet]; + + //System.out.println("states:"+ states); + //System.out.println("P1:"+ P1); + + invIndexMap.put( new Integer( ix ), P1 ); + indexMap.put( P1, new Integer( ix )); + ix = ix + 1; + delta/*Map*/.put( P1, {trans = new HashMap(); trans}); + + //Console.println("...beginning 4" ); + // labelled transitions + val it = _labels.iterator(); + //Console.println("it = "+it ); + //Console.println(it.hasNext()); + + while( it.hasNext() ) { + //Console.println("...beginning 5" ); + //Console.flush; + val label = it.next(); + //Console.print( "Label: " + label +" "); + // Qdest will contain all states reachable via `label' + // from some nfa state in P1; + val Qdest = nfa.getSide( P1, label ); + //Console.println("Qdest:"+Qdest); + if( !states.contains( Qdest ) ) { + states.add( Qdest ); + ////System.out.print(" (added)" ); + rest.push( Qdest ); + ////System.out.print(" (pushed)"); + + //Console.println("nfa.containsFinal("+Qdest+") =="+nfa.containsFinal( Qdest )); + + if( nfa.containsFinal( Qdest ) ) + this.finals.put( Qdest, nfa.finalTag( Qdest )); + ////System.out.print(" (added final)"); + + } + ////System.out.println(".Qdest"); + + trans.put( label, Qdest ); + // //System.out.println( "Qdest: " + Qdest); + //Console.println("Y marks"); + } + + // default transitions + + val defTarget: TreeSet = nfa.defaultq( P1 ).asInstanceOf[TreeSet]; + //System.out.println("defTarget:"+defTarget); + deftrans.put( P1, defTarget ); + + //Console.println("X marks 0"); + + if( !states.contains( defTarget ) ) { + //Console.println("X marks 1"); + + states.add( defTarget ); + rest.push( defTarget ); + //Console.println("nfa.containsFinal("+defTarget+")"+nfa.containsFinal( defTarget )); + if( nfa.containsFinal( defTarget ) ) + this.finals.put( defTarget, nfa.finalTag( defTarget )); + } + + //Console.println("X marks"); + } + + //Console.println("...continuing"); + + // <DEBUG> + //printBefore( states, deftrans ); + + // </DEBUG> do not call printBefore after this point + // //System.out.println("indexMap: "+indexMap); + + this._nstates = states.size(); + _deltaq = new Array[HashMap]( _nstates ); + _defaultq = new Array[Integer]( _nstates ); + + // we replace Set[Set[Integer]] by its index and clean up + + val jt = states.iterator(); + while(jt.hasNext()) { + val state = jt.next().asInstanceOf[TreeSet]; + val state_x = indexMap.get( state ).asInstanceOf[Integer]; + + val defTarget = deftrans.get( state ).asInstanceOf[TreeSet]; + var defTarget_x: Integer = null; + if( null != defTarget) { + defTarget_x = indexMap.get( defTarget ).asInstanceOf[Integer]; + ////System.out.println("deftarget" + defTarget); + } else + defTarget_x = NODEFAULT; + + ////System.out.print(state.toString() + " --> " + state_x); + //System.out.println(" deftarget " + defTarget + " --> "+defTarget_x); + + trans = delta/*Map*/.get( state ).asInstanceOf[HashMap]; + val newTrans = new HashMap(); + val labs = _labels.iterator(); + while(labs.hasNext()) { + val label = labs.next(); + val target = trans.get( label ).asInstanceOf[TreeSet]; + var target_x: Integer = null; + if( null != target ) { + // //System.out.println("target :"+target); + target_x = indexMap.get( target ).asInstanceOf[Integer]; + + if( target_x.intValue() != defTarget_x.intValue() ) { + // replace target by target_x + // (use type-unawareness) + newTrans.put( label, target_x ); + } + trans.remove( label ); + } + + } + _deltaq( state_x.intValue() ) = newTrans; + _defaultq( state_x.intValue() ) = defTarget_x; + + delta/*Map*/.remove( state ); + deftrans.remove( state ); + + } + //Console.println("determinize::: finals"+finals); + val oldfin: TreeMap = finals; + this.finals = new TreeMap(); + var kt = oldfin.keySet().iterator(); + while(kt.hasNext()) { + val state = kt.next().asInstanceOf[TreeSet]; + val state_x = indexMap.get( state ).asInstanceOf[Integer];; + this.finals.put( state_x, oldfin.get( state ) ); // conserve tags + } + + // clean up, delete temporary stuff + /* + // we cannot clean up, indexmap is needed later + for( Iterator it = states.iterator(); it.hasNext(); ) { + ((TreeSet) it.next()).clear(); + } + */ + states.clear(); + + //Console.println("...done"); + //minimize(); + } + + + + def isDead(state: Int): Boolean = { + return state == _nstates - 1; // by construction + } + + def isDead(state: Integer): Boolean = { + return state.intValue() == _nstates - 1; // by construction + } + + + /** returns target of the transition from state i with label label. + * null if no such transition exists. + */ + def delta(i: Int, label: Label): Integer = { + var target:Integer = null; + label match { + case DefaultLabel() => + if (! hasDefault(i)) + return null; + return _defaultq( i ).asInstanceOf[Integer] ; + case SimpleLabel( _ ) | TreeLabel( _ ) => + return _deltaq( i ).get( label ).asInstanceOf[Integer] ; + /*case LPair( Integer state, Label lab ): + return state; + */ + case _ => + scala.Predef.error("whut's this: label="+label+", class "+label.getClass()); + } + } + + def delta(i: Integer, label: Label): Integer = + delta(i.intValue(), label); + + /** should maybe in nfa, not here + */ + /*static */ + protected def smallestFinal( nfa: NondetWordAutom, states:TreeSet ): Integer = { + + var min = Integer.MAX_VALUE ; + val it = states.iterator(); + while (it.hasNext()) { + val state = it.next().asInstanceOf[Integer]; + if( nfa.isFinal( state ) && (state.intValue() < min )) + min = state.intValue(); + } + if (min == Integer.MAX_VALUE) + scala.Predef.error("I expected a final set of states"); + return new Integer( min ); + } + + protected def allSetsThatContain( ndstate: Integer ): Vector = { + val v = new Vector(); + val it = indexMap.keySet().iterator(); + while(it.hasNext()) { + val ndstateSet = it.next().asInstanceOf[TreeSet]; + if( ndstateSet.contains( ndstate )) + v.add( ndstateSet ); + } + return v; + } + + + protected def filterItOutQuoi( dLeft: DetWordAutom, npTarget: Npair,lab:LPair , nsrc:TreeMap ):Unit = { + val theLabel = lab.lab; + val ntarget = lab.state; + + // e.g.[2,(3),4] --> 7 + val dstate = dLeft.indexMap.get( npTarget.nset ).asInstanceOf[Integer]; + + // eg. 3 -> [3] [2,3] + val targets:Vector = dLeft.allSetsThatContain( ntarget ); + + ////System.out.println( targets+", of these " ) ; + + // filter out those source states which arrive here... + val su = targets.iterator(); + while(su.hasNext()) { + val nset = su.next().asInstanceOf[TreeSet]; + val ddelta = dLeft.deltaq( nset ).asInstanceOf[HashMap]; + + // ... at THIS dstate + if(ddelta.get( theLabel ).asInstanceOf[Integer] == dstate ) { + + val np1 = new Npair( ntarget, nset ); + + ////System.out.print( np1.toString( dLeft.indexMap )); + + if( WHICH_LONGEST_MATCH == FIRST ) + addTransitionFLM( nsrc, np1 ); + else + addTransitionLLM( nsrc, np1 ); + } + + } + } + + /** all default transitions from sets that contain nq to npTarget + */ + protected def filterItOutQuoiDefault( dLeft: DetWordAutom ,npTarget:Npair , nq:Integer , nsrc:TreeMap ): Unit = { + + + ////System.out.println( "npTarget = " + npTarget ) ; + + val allSources = dLeft.allSetsThatContain( npTarget.nstate ); + val it = allSources.iterator(); + while(it.hasNext()) { + + // e.g.[2,(3),4] --> 7 + //Integer dstate = (Integer) dLeft.indexMap.get( npTarget.nset ); + + val dstate = dLeft.indexMap.get( it.next() ).asInstanceOf[Integer]; + + //System.out.println( "dstate = " + dstate ) ; + + //assert dstate != null; + + // eg. 3 -> [3] [2,3] + val targets = dLeft.allSetsThatContain( nq ); + + //System.out.println( "targets: " + targets ) ; + + // filter out those source states which arrive here... + val su = targets.iterator(); + while(su.hasNext()) { + val nset = su.next().asInstanceOf[TreeSet]; + val ddef = dLeft.defaultq( nset ); + + //System.out.println( "ddef ="+ddef ); + + // ... at THIS dstate + if( ddef == dstate ) { + + val np1 = new Npair( nq, nset ); + + // print target + //System.out.print( np1.toString( dLeft.indexMap )); + + if( WHICH_LONGEST_MATCH == FIRST ) + addTransitionFLM( nsrc, np1 ); + else + addTransitionLLM( nsrc, np1 ); + + } + + } + } + } + + /** this implements the first longest match policy + */ + protected def addTransitionFLM( nsrc:TreeMap , np:Npair ): Unit= { + val np2 = nsrc.get( np.nset ).asInstanceOf[Npair ]; + + // (policy) first longest match + if(( np2 == null ) + ||( np2.nstate.intValue() > np.nstate.intValue())) { + nsrc.put( np.nset, np ); + } + + } + + /** this implements the last longest match policy (!) + */ + protected def addTransitionLLM(nsrc: TreeMap, np: Npair ): Unit = { + val np2 = nsrc.get( np.nset ).asInstanceOf[Npair]; + + // (policy) first longest match + if(( np2 == null ) + ||( np2.nstate.intValue() < np.nstate.intValue())) { + nsrc.put( np.nset, np ); + } + + } + + + /** build a deterministic right to left transducer from the args + */ + def this(right: NondetWordAutom, left:NondetWordAutom, dLeft: DetWordAutom ) = { + this(); + + /* System.out.println("DetWordAutom.<init>(nfa,nfa,dfa)"); + System.out.println("nfa-left:");left.print(); + System.out.println("nfa-right:");right.print(); + System.out.println("dLeft:"+dLeft.print()); + System.out.println("dLeft.finals"+dLeft.finals); + */ + this.indexMap = dLeft.indexMap; + this.invIndexMap = dLeft.invIndexMap; + // fix indexMap + /* // unnecessary + TreeSet q0 = new TreeSet(); + q0.add( new Integer( 0 )); + indexMap.put( q0, new Integer( 0 )); + //System.out.println("check out the indexMap!" + indexMap); + */ + + val visited_n = new TreeSet( new NpairComparator() ); + val rest = new Stack(); + + // right is "nearly deterministic" + // we can follow reverse traces paths by using dLeft.indexMap + + // start with right.initials, left.final, dLeft.final + val it = dLeft.finals.keySet().iterator(); + while(it.hasNext()) { + val fstate = it.next().asInstanceOf[Integer]; + val nfstate = invIndexMap.get( fstate ).asInstanceOf[TreeSet]; + //System.out.print( "final state:"+fstate); + //System.out.print( " correspond to set of states:"+ nfstate ); + + val min_ndstate: Integer = smallestFinal( left, nfstate ); + + val npair:Npair = new Npair( min_ndstate, nfstate ); + + //System.out.println( " smallest final of these: "+ min_ndstate ); + + + //System.out.println( "push final nfa state "+npair.toString( dLeft.indexMap )); + + if( !visited_n.contains( npair )) { + visited_n.add( npair ); + rest.push( npair ); + } + } + + val ratLab = new HashMap(); // maps nset to label,HashMap + val ratDelta = new HashMap(); // maps nset to Vector[ NP ]targets + + val ratDefault = new HashMap(); // maps nset to NP (one target) + + var ix = 1; + val ix_initial = rest.clone().asInstanceOf[Stack]; + var ix_final = new TreeSet( new NpairComparator() );; + + val newIndexMap = new TreeMap( new NpairComparator() ); + + while( !rest.isEmpty() ) { + + val npair = rest.pop().asInstanceOf[Npair]; + newIndexMap.put( npair, new Integer(ix)); + + ratDelta.put( npair, new Vector() ); + + if( npair.nset.contains( new Integer( 0 )) ) { + ix_final.add( npair ); + } + ix = ix + 1; + + //System.out.println(" popped "+npair.toString( dLeft.indexMap )); + + ////System.out.print(" binders: "); + ////System.out.print( right.qbinders[ npair.nstate.intValue() ] ); + + val delta = right.deltaq( npair.nstate ); + + ////System.out.print(" we could have arrived : "); + //search the delta for target invIndexMap + + val labelToNset = new HashMap(); + val labelToFrom = new HashMap(); + + // maps nsets to the active nstates + var nsrc = new TreeMap( new StateSetComparator() ); + + // berry-sethi construction assures that + // there is only one label for outgoing transitions + var theLabel:Label = null; + + // collect all transition possible in the DFA + val jt = delta.keySet().iterator(); + while(jt.hasNext()) { + val lab = jt.next().asInstanceOf[LPair]; + + // lab.state is the target in the NFA + + if( null == theLabel ) { + ratLab.put( npair, lab.lab ); + ////System.out.print(" with \""+lab.lab+"\" "); + } + theLabel = lab.lab ; + + ////System.out.print("\nfrom n" + lab.state +" ... "); + + // these are too many, filter out those that exist in DFA + + filterItOutQuoi( dLeft, npair, lab, nsrc ); + + } + + + ////System.out.println( "---" ); + + ////System.out.println("all sources: "); + + // !! first longest match + val ut = nsrc.keySet().iterator(); + while(ut.hasNext()) { + val nset = ut.next().asInstanceOf[TreeSet]; + val np2: Npair = nsrc.get( nset ).asInstanceOf[Npair] ; + + //assert( np2 != null ); + ////System.out.println("target: n"+npair.nstate+" via: "+theLabel+" from "+ np2.toString( dLeft.indexMap ));// nset:"+nset+ " namely state n"+ dest); + + val v = ratDelta.get( npair ).asInstanceOf[Vector]; + + v.add( np2 ); + + if( !visited_n.contains( np2 ) ) { + + visited_n.add( np2 ); + rest.push( np2 ); + } + + } + + //System.out.println("default sources: "); + + // maps nsets to the active nstates + nsrc = new TreeMap( new StateSetComparator() ); + + // now for all default transitions that arrive at this nfa state + val defqs = right.defaultq( npair.nstate ); + val kt = defqs.iterator(); + while( kt.hasNext() ) { + val nq = kt.next().asInstanceOf[Integer]; + //System.out.println("checking nq="+nq); + filterItOutQuoiDefault( dLeft, npair, nq, nsrc ); + //System.out.println( "nsrc after "+nq+" is "+nsrc ); + } + + //System.out.println( "defqs :"+defqs ); + //System.out.println( "nsrc :"+nsrc ); + var nut = nsrc.keySet().iterator(); + while(nut.hasNext()) { + + val np2 = nsrc.get( nut.next() ).asInstanceOf[Npair]; + + var v = ratDefault.get( npair ).asInstanceOf[Vector] ; + if( v == null ) + ratDefault.put( npair, {v = new Vector(); v} ); + v.add( np2 ); + + if( !visited_n.contains( np2 ) ) { + + visited_n.add( np2 ); + rest.push( np2 ); + } + + } + + ////System.out.println("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); + + } + + // Renumbering + + ////System.out.println( "output: a dfa with "+ix+"states"); + + // FIX: empty regular expression (as in "List()") is valid + //assert ( !ix_final.isEmpty() ) : "no final states found"; + + ////System.out.println( "final state:"+ix_final); + + //System.out.println( "indexMap: " +indexMap); + //System.out.println( "newIndexMap: " +newIndexMap); + this.finals = new TreeMap(); + this._nstates = ix; + val dratDelta = new Array[HashMap]( ix ); + qbinders = new Array[Vector]( ix ); + _labels = new HashSet(); + val kit = ratDelta.keySet().iterator(); + while(kit.hasNext()) { + val np = kit.next().asInstanceOf[Npair]; + + //System.out.print( "\nstate: "+np); + val ndset = np.nset; + val dstate = newIndexMap.get( np ).asInstanceOf[Integer]; + //assert dstate != null : "no dstate for "+np.toString(dLeft.indexMap); + + //System.out.print(" binders:"); + + qbinders( dstate.intValue() ) = left.qbinders( np.nstate.intValue() ); + + //System.out.print( qbinders[dstate.intValue() ]); + + //System.out.println(" transitions:"); + if( ix_final.contains( np ) ) { + val fin_ix = newIndexMap.get( np ).asInstanceOf[Integer]; + finals.put( fin_ix, new Integer( 0 )); + } + + val lab = ratLab.get( np ).asInstanceOf[Label]; + val v = ratDelta.get( np ).asInstanceOf[Vector]; + + val ddelta = new HashMap(); + + // v might be null if there are only default transitions + if( v != null ) { + val it2 = v.iterator(); + while(it2.hasNext()) { + + val np2= it2.next().asInstanceOf[Npair]; + //System.out.print( "("+lab+","+np2+") " ); + val ddestR = newIndexMap.get( np2 ).asInstanceOf[Integer]; + val ddest = indexMap.get( np2.nset ).asInstanceOf[Integer]; + //assert ddest != null : + //"no ddest for " + //+np2.toString(dLeft.indexMap); + + val newLab = new LPair(ddest, lab); + ddelta.put( newLab, ddestR ); + _labels.add( newLab ); + + } + dratDelta( dstate.intValue() ) = ddelta; + + } + } + var itt = ratDefault.keySet().iterator(); + while(itt.hasNext()) { + val np = itt.next().asInstanceOf[Npair]; + val dstate = newIndexMap.get( np ).asInstanceOf[Integer]; + + //System.out.print("\nstate: "+np+" default trans: "); + + val v = ratDefault.get( np ).asInstanceOf[Vector]; + val ut = v.iterator(); + while(ut.hasNext()) { + val np2 = ut.next().asInstanceOf[Npair]; + val targetL = indexMap.get( np2.nset ).asInstanceOf[Integer];; + val targetR = newIndexMap.get( np2 ).asInstanceOf[Integer];; + + val defLab = new LPair( targetL, DefaultLabel() ); + + _labels.add( defLab ); + //System.out.print( "("+defLab+","+np2+") " ); + + var d = dratDelta( dstate.intValue() ); + if( d == null ) + dratDelta( dstate.intValue() ) = {d = new HashMap(); d}; + + d.put( defLab, targetR ); + } + } + + _deltaq = dratDelta; + + val hmap = new HashMap(); + + // final states of left are initial states of right + // problem: still need to choose the one + + while( !ix_initial.isEmpty() ) { + val np = ix_initial.pop().asInstanceOf[Npair]; + + val i = newIndexMap.get( np ).asInstanceOf[Integer]; //R-state + val dtarget = indexMap.get( np.nset ).asInstanceOf[Integer];// left-d-state + + hmap.put( dtarget, i ); + } + _deltaq( 0 ) = hmap; // careful, this maps Int to Int + + qbinders( 0 ) = new Vector(); + //((Vector[])defaultq)[ 0 ] = new Vector(); is null + //printBeforeRAT( dratDelta ); + + } + + def printBeforeRAT1(str: String): Unit = { + val tmp = new StringBuffer( str ); + var j = tmp.length(); + while(j < 20) { + tmp.append(" "); + j = j + 1; + } + Console.println( tmp.toString() ); + } + + def printBeforeRAT( dratDelta: Array[HashMap] ): Unit = { + //System.out.println(); + printBeforeRAT1( "dratDelta" ); + printBeforeRAT1( "[index]" ); + //System.out.println(); + var i = 0; + while(i < dratDelta.length) { + if( isFinal( i )) + printBeforeRAT1( "*"+i ); + else + printBeforeRAT1( " "+i ); + + //System.out.println( dratDelta[ i ] ); + i = i + 1 + } + } + + /** you may only call this before the set[set[...]] representation + * gets flattened. + */ + def printBefore(states: TreeSet, deftrans: HashMap): Unit = { + var trans: HashMap = null; + Console.println(states); + val it = states.iterator(); + while (it.hasNext()) { + val state = it.next().asInstanceOf[TreeSet]; + Console.print("state:" + state.toString() + " transitions "); + trans = delta/*Map*/.get( state ).asInstanceOf[HashMap]; + val labs = _labels.iterator(); + while(labs.hasNext()) { + val label = labs.next(); + val target = trans.get( label ).asInstanceOf[TreeSet]; + Console.print( " (" + label.toString() + + "," + target.toString()+")"); + } + Console.print("default trans"+deftrans.get(state)); + Console.println; + } + Console.println("final states:" + finals); + } +} + +} diff --git a/src/compiler/scala/tools/nsc/matching/LeftTracers.scala b/src/compiler/scala/tools/nsc/matching/LeftTracers.scala new file mode 100644 index 0000000000..b58e11b4cb --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/LeftTracers.scala @@ -0,0 +1,232 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ + +package scala.tools.nsc.matching; + +import java.util._ ; + +import scala.tools.nsc.util.Position; + +trait LeftTracers: TransMatcher { + +import global._; + +abstract class LeftTracerInScala extends Autom2Scala { + + val selector: Tree; + val elementType: Type; + + /** symbol of the accumulator ( scala.SequenceList ) + */ + var accumSym: Symbol = _; + var accumType: Type = _; + var accumTypeArg: Type =_ ; + + def _accumType(elemType: Type): Type = { + SeqTraceType( elemType ); + } + + protected def initializeSyms(): Unit = { + this.funSym = owner.newLabel( pos, fresh.newName( "left" )); + + this.iterSym = owner.newVariable( pos, fresh.newName( "iter" )) + .setInfo( _seqIterType( elementType ) ) ; + + this.stateSym = owner.newVariable( pos, fresh.newName( "q" )) + .setInfo( definitions.IntClass.info ) ; + + this.accumType = _accumType( elementType ); + this.accumTypeArg = accumType.typeArgs( 0 ); + this.accumSym = owner.newVariable( pos, // accumulator + fresh.newName( "acc" )) + .setInfo( accumType ); + + //this.funSym + // .setInfo( new MethodType( new Symbol[] { + // accumSym, iterSym, stateSym}, + // accumType)); + + this.funSym.setInfo( + MethodType( + scala.List ( // dummy symbol MethodType + definitions.IntClass.info, + accumType + ), + accumType) + ); + + //funSym.newValueParameter( pos, fresh.newName( "q" )) + //.setInfo(definitions.IntClass.info), + //funSym.newValueParameter( pos, fresh.newName( "acc" )) + //.setInfo( accumType ) ), + // accumType)); // result type = List[T] + + this.resultSym = owner.newVariable(pos, fresh.newName("trace")) + .setInfo( accumType ) ; + + this.curSym = owner.newVariable( pos, "cur" ) + .setInfo( elementType ); + + this.hasnSym = owner.newVariable( pos, nme.hasNext ) + .setInfo( definitions.BooleanClass.info ); + + } + + /* should throw an exception here really, e.g. MatchError + */ + override def code_fail() = Ident( accumSym ); + + /** returns translation of transition with label from i. + * returns null if there is no such transition(no translation needed) + */ + override def code_delta(i: Int, label: Label): Tree = { + val target = dfa.delta( i, label ); + + /* + System.out.println("LeftTracer:calling dfa.delta("+i+","+label+")"); + System.out.println("result: "+target); + */ + if( target == null ) + null; + else { + // (optimization) that one is a dead state (does not make sense for tracer) + /* + if( target == dfa.nstates - 1 ) + return code_fail(); + */ + + /* + Tree newAcc = newSeqTraceCons(new Integer(i), + currentElem(), + _ref( accumSym )); + */ + val hd = gen.mkNewPair( Literal(i), currentElem() ); + + val newAcc = gen.mkNewCons(hd, Ident(accumSym )); + + //return callFun( new Tree[] { newAcc , _iter(), mkIntLit( pos, target )} ); + callFun( scala.List( Literal(target.intValue() ), newAcc ) ); + } + } + + + def code_body(): Tree = { + + var body = code_error(); // never reached at runtime. + + // state [ nstates-1 ] is the dead state, so we skip it + + //`if( state == q ) <code_state> else {...}' + var i = dfa.nstates() - 2; + while (i >= 0) { + body = code_state(i, body); + i = i - 1; + } + loadCurrentElem(body); + } + + /** return code for state i of the dfa SAME AS IN SUPER, ONLY SINK IS GONE + */ + def code_state(i: Int, elseBody: Tree): Tree = { + + var runFinished: Tree = null; // holds result of the run + var finalSwRes: Int = 0; + + runFinished = run_finished(i); + + var stateBody: Tree = null ; // action(delta) for one particular label/test + + // default action (fail if there is none) + + stateBody = code_delta( i, DefaultLabel()); + + if( stateBody == null ) + stateBody = code_fail(); + // transitions of state i + + val trans = dfa.deltaq( i ); + val labs = dfa.deltaq( i ).keySet().iterator(); + while(labs.hasNext()) { + val label = labs.next(); + val next = trans.get( label ).asInstanceOf[Integer]; + + val action = code_delta( i, label.asInstanceOf[Label] ); + + if( action != null ) { + stateBody = If( currentMatches(label.asInstanceOf[Label]), + action, + stateBody); + } + } + stateBody = If(Negate(Ident(hasnSym)), + runFinished, + stateBody ); + If( Equals( _state(), Literal( i )), + stateBody , + elseBody ); + } + + def getTrace(): Tree = { + + initializeSyms(); + + Block(scala.List( + ValDef( iterSym, newIterator( selector )), + ValDef( stateSym, Literal( 0 ) ), + ValDef( accumSym, gen.mkNil /*mkNil( pos )*/), + ValDef( resultSym, + LabelDef( this.funSym, + scala.List ( + stateSym, + accumSym + ), code_body() /* code_body_new ? */ )) + ), + Ident( resultSym )); + } + + // calling the AlgebraicMatcher here + override def _cur_match(pat: Tree): Tree = { + //return mkBooleanLit(pos, true); + + //System.out.println("calling algebraic matcher on type:" + pat.type); + + val m = new PartialMatcher { + val owner = LeftTracerInScala.this.owner; + val selector = currentElem(); + + // result type definitions.BooleanClass.info ); + } + + pat match { + case Sequence(pats) if containsBinding(pat) => + //scala.Predef.error("should not happen?!"); + null; // Literal(true); ?! + case _ => + am.construct(m, scala.List ( + CaseDef( pat, Literal( true )), + CaseDef( Ident( nme.WILDCARD ), Literal(false)) ), + false); + am.toTree(); + } + } + + + /** return the accumulator + last state + */ + override def run_finished(state: Int): Tree = { + val hd = gen.mkNewPair( Literal(state), EmptyTree); + //System.err.println(hd.type); + gen.mkNewCons(hd, Ident( accumSym )); +/* + mkNewCons(pos, + accumTypeArg, + hd, + Ident( accumSym )); +*/ + } + +} // TracerInScala +} // LeftTracers diff --git a/src/compiler/scala/tools/nsc/matching/MatcherLabels.scala b/src/compiler/scala/tools/nsc/matching/MatcherLabels.scala new file mode 100644 index 0000000000..06c8cd2baa --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/MatcherLabels.scala @@ -0,0 +1,126 @@ +package scala.tools.nsc.matching ; + +[_trait_] abstract class MatcherLabels: TransMatcher { + + import global._ ; + + /** + * This class represents the label that a transition in an automaton + * may carry. These get translated to specific (boolean) tests + */ + + class Label { + + //case class RLabel(Object rstate, Label lab, Symbol vars[]); + + override def hashCode(): Int = this match { + case DefaultLabel() => + return 0; + case SimpleLabel(lit) => + return lit.value.hashCode(); + case TreeLabel(pat) => + // if pat is an Apply, than this case can only be correctly + // handled there are no other similar Applys (nondeterminism) + return pat.tpe.hashCode(); + case TypeLabel(tpe) => + return tpe.hashCode(); + case _ => + return super.hashCode(); + } + + override def equals( o: Any ): Boolean = { + if( !(o.isInstanceOf[Label] )) + return false; + val oL = o.asInstanceOf[Label]; + //System.out.print(this + " equals " + oL); + this match { + case DefaultLabel()=> + oL match { + case DefaultLabel() => + return true; + case _ => false; + } // + case SimpleLabel( lit ) => + oL match { + case SimpleLabel( lit2 ) => + return (/*(lit.kind == lit2.kind) + && */lit.value.equals( lit2.value )); + case _ => false; + } + + case TreeLabel( pat ) => + oL match { + case TreeLabel( pat2 ) => + pat match { + case Apply( _, _ ) => + pat2 match { + case Apply( _, _ ) => + return (treeInfo.methPart/*methSymbol?*/( pat ) + == treeInfo.methPart/*methSymbol*/( pat2 )); + } + case _ => false; + } + case _ => false + } + + case TypeLabel(tpe) => + oL match { + case TypeLabel( tpe2) => + return tpe.equals(tpe2); + case _ => false; + } + case LPair(state, lab) => + oL match { + case LPair(state2, lab2) => + return state.equals(state2) && lab.equals(lab2); + case _ => false; + } + case _ => return false; + } + } + + + def toString2(): String = { + val ext = System.getProperty("extendedMatching"); + if ((ext != null) && ext.equals("true")) { + this match { + case DefaultLabel() => + return "<>:p"+p; + case SimpleLabel( lit ) => + return lit.toString()+":p"+p; + case TreeLabel( pat ) => + return pat.tpe.toString()+":p"+p; + + case _ => scala.Predef.error("this never happens"); + } + } + scala.Predef.error("this never happens"); + } + + override def toString(): String = this match { + case DefaultLabel() => + "<>"; + case SimpleLabel( lit) => + lit.toString(); + case TreeLabel(pat) => + pat.toString(); + case TypeLabel(tpe) => + tpe.toString(); + case LPair(state, lab) => + "(" + state.toString() + "," + lab.toString() + ")"; + case _ => + scala.Predef.error("this never happens"); + } + + val p = -1; // tree state - only needed for extended matching + +} + + case class DefaultLabel() extends Label; + case class SimpleLabel(lit: Literal) extends Label; + case class TreeLabel(pat: Tree) extends Label; // Apply, Sequence + case class TypeLabel(tpe: Type) extends Label; // Apply, Sequence + case class LPair(state: Integer, lab: Label) extends Label; + +} + diff --git a/src/compiler/scala/tools/nsc/matching/NondetWordAutoms.scala b/src/compiler/scala/tools/nsc/matching/NondetWordAutoms.scala new file mode 100644 index 0000000000..cd1d8b576e --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/NondetWordAutoms.scala @@ -0,0 +1,522 @@ +package scala.tools.nsc.matching ; +import java.util._ ; + +trait NondetWordAutoms { +/** a nondeterministic word automaton. + * states are represented (implicitly) as Integer objects. + */ +class NondetWordAutom { + // BEGIN stuff from FiniteAutom + + //final static Integer FINTAG = new Integer(0); + + /** number of states */ + var nstates: int =_; + + /** the 'alphabet' */ + var labels: HashSet = _; + + /** the set of final states, here as a TreeMap */ + var finals: TreeMap = _; + + /** dfa: HashMap trans: Object -> Integer + * nfa: HashMap trans: Object -> Vector [ Integer ] + * + * nfa: Integer ->(Object -> Vector [ Int ]) + * [q] |->( a |-> { q' | (q,a,q') in \deltaright } ) + * + * dfa: Integer ->(Object -> Int) + * [q] |->( a |-> q' | \deltaright(q,a) = q' } ) + */ + + var _deltaq:Array[HashMap] = _; + + var _defaultq:Array[Vector] = _; // this gives the default transitions + + //public HashMap deltaq[]; + + // --- accessor methods + + /** returns number of states + def nstates(): int = { + return nstates; + } + */ + + /** returns the labels + def labels(): HashSet = { + return _labels; + } + */ + + /** returns the transitions + */ + def deltaq( state: int ):HashMap = { + return _deltaq( state ); + } + + /** returns the transitions + */ + def deltaq( state: Integer ): HashMap = { + return _deltaq( state.intValue() ); + } + + /** returns the transitions + */ + def defaultq( state: int ): Vector = { + return _defaultq( state ); + } + + /** returns the transitions + */ + def defaultq( state:Integer ): Vector = { + return _defaultq( state.intValue() ); + } + + + /** returns true if the state is final + */ + def isFinal( state:int ): boolean = { + return ((finals != null) + && (finals.get( new Integer( state )) != null)); + } + + /** returns true if the state is final + */ + def isFinal( state:Integer ): boolean = { + return ((finals != null) && finals.containsKey( state )); + } + + /** returns true if the state is final + */ + def finalTag( state: Integer ): Integer = { + return finals.get( state ).asInstanceOf[Integer]; + } + + + def finalTag( state:int ): Integer = { + return finals.get( new Integer (state )).asInstanceOf[Integer]; + } + + /** returns true if the set of states contains at least one final state + */ + def containsFinal( Q:TreeSet ): boolean = { + var it = Q.iterator(); + while(it.hasNext()) { + if( isFinal( it.next().asInstanceOf[Integer])) + return true; + } + return false; + } + + + /** returns true if there are no finite states + */ + def isEmpty(): boolean = finals.isEmpty(); + + // END stuff from FiniteAutom + + + // inherited from FiniteAutom + + // set of *distinct* objects that may label transitions + // see Object.hashCode() to see why this works + + //HashSet labels; + //TreeMap finals; + + var initials: TreeSet = _; // ? need this ? + // --- + + // Object deltaq --> + // HashMap deltaq[]; // this gives the transitions of q; + + var leftTrans: boolean = _; + var rightTrans: boolean = _; + + /** if true, this automaton behaves as a special left transducer. + * if a run succeeds, the result is not "true" but the entire + * run of the automaton as a sequence of (label,state) - pairs. + * used for binding variables. + */ + def producesRun(): boolean = { + return leftTrans; + } + + def consumesRun(): boolean = { + return rightTrans; + } + + def initial( i: Integer ): boolean = { + return initials.contains( i ); + } + var qbinders: Array[Vector] = _; + + /** returns all states accessible from Qsrc via label. + * used by class DetWordAutomaton. + */ + def getSide ( Qsrc:TreeSet , label:Object ): TreeSet = { + //Console.println("NWA::getSide(Qsrc="+Qsrc); + val Qdest = new TreeSet(); + var it = Qsrc.iterator(); + while(it.hasNext()) {// state + val q = it.next().asInstanceOf[Integer].intValue(); + val ps = deltaq( q ).get( label ).asInstanceOf[Vector]; + //Console.println("deltaq(q) = "+deltaq(q)); + //Console.println("_deltaq(q) = "+_deltaq(q)); + //Console.println("ps = "+ps); + if( null != ps ) { + Qdest.addAll( ps ); + } + //Console.println("defq = "+_defaultq( q )); + Qdest.addAll( _defaultq( q ) ); + } + //Console.println("DONE-NWA::getSide"); + return Qdest; + } + + /** returns the transitions + */ + def defaultq( P1: Set ): Object = { + val defTarget = new TreeSet(); + var p1 = P1.iterator(); + while(p1.hasNext()) { + val q1 = p1.next().asInstanceOf[Integer].intValue(); + //System.out.println(" q1:"+q1+ " defa: "+defaultq( q1 )); + defTarget.addAll( _defaultq( q1 ) ); + } + return defTarget; + } + + + /** first match policy: among several final states, the smallest one is + * chosen. used by class DetWordAutomaton + */ + def finalTag( Q:Set ): Integer = { + + var min = Integer.MAX_VALUE ; + var it = Q.iterator(); + while(it.hasNext()) { + val state = it.next().asInstanceOf[Integer]; + val tag = finals.get( state ).asInstanceOf[Integer]; + if( tag != null ) { + if( tag.intValue() < min ) + min = tag.intValue(); + } + } + + if ( min == Integer.MAX_VALUE ) + scala.Predef.error( "there should be a final state "); + + return new Integer( min ); + } + + /* + void tmap(int offs, TreeMap t) = { + TreeMap nt = new TreeMap(); + for(Iterator it = t.keySet().iterator(); it.hasNext(); ) = { + Integer key = (Integer) it.next(); + Integer newkey = new Integer( key.intValue() + offs ); + Integer val = (Integer) t.get( key ); + Integer newval = new Integer( val.intValue() + offs ); + + nt.put( newkey, newval ); + } + return nt; + } + */ + // hashmaps, treemaps + def mapmap(src:Map, offset:int , dest:Map , mapkeys:boolean , mapvals:boolean ): Map = { + var it = src.keySet().iterator(); + while(it.hasNext()) { + var key = it.next(); + var value = src.get( key ); + if( mapkeys ) key = new Integer( key.asInstanceOf[Integer].intValue() + + offset ); + if( mapvals ) value = vmap( offset, value.asInstanceOf[Vector] ) ; + /* new Integer( ((Integer)val).intValue() + + offset ); + */ + dest.put( key, value ); + } + return dest; + } + + def vmap(offs:int , v:Vector ): Vector = { + if( v == null ) + return null; + var res = new Vector( v.size() ); + var it = v.iterator(); + while(it.hasNext()) { + val item = it.next().asInstanceOf[Integer]; + res.add( new Integer( item.intValue() + offs )); + } + return res; + + } + + /* + void relocate_defaultq( int offs, Vector _defaultq[] ) = { + for( int i = 0; i < this.nstates; i++ ) = { + _defaultq[ i + offset ] = vmap( offset, ((Vector[])defaultq)[ i ]); + } + } + */ + + /** copies the values in the fields of this object into the + * arguments, possibly adding an offset. + */ + def relocate( offset:int, _finals:TreeMap, _deltaq1:Array[HashMap], _defaultq1:Array[Vector], _qbinders1:Array[Vector] ): Unit = { + + mapmap( finals, offset, _finals, true, false); + var i = 0; + while(i < this.nstates) { + + _deltaq1 ( i + offset ) = + mapmap( deltaq( i ), offset, new HashMap(), false, true).asInstanceOf[HashMap]; + + _defaultq1( i + offset ) = vmap( offset, this.defaultq( i ) ); + i = i + 1; + } + if ((_qbinders1 != null) &&( qbinders != null )) { + i = 0; + while(i < this.nstates ) { + //System.out.println("hallo"+qbinders); + //System.out.println("qbinders[ i ] :"+qbinders[ i ]); + //assert _qbinders != null; + //System.out.println("_qbinders :"+_qbinders); + + _qbinders1( i + offset ) = qbinders( i ); + i = i + 1 + } + } + } + + + /** if there is more than one initial state, a new initial state + * is created, with index 0 + */ + def normalize( initials:TreeSet , reloc:boolean ): Unit = { + //if( initials.size() == 1 ) + // return; + + var idelta = new HashMap(); + var idefault = new TreeSet(); + + var q0 = new Integer( 0 ); + + var it = initials.iterator(); + while(it.hasNext()) { + + val ostate = it.next().asInstanceOf[Integer]; + + val finTag = finals.get( ostate ).asInstanceOf[Integer] ; + if(( finTag != null ) && ( finals.get( q0 ) == null)) + finals.put( q0, finTag ); + + + var tmp = deltaq( ostate ); + + if( reloc ) + tmp = mapmap( tmp, 1, new HashMap(), false, true ).asInstanceOf[HashMap]; + + val labs = tmp.keySet().iterator(); + while(labs.hasNext()) { + val lab = labs.next(); + var itarget = idelta.get( lab ).asInstanceOf[Vector]; + if( null == itarget ) + idelta.put( lab, {itarget = new Vector(); itarget}); + val otarget = tmp.get( lab ).asInstanceOf[Vector]; + itarget.addAll( otarget ); + } + //System.out.println( "normalize:defaultq[ "+ostate+" ] "+((Vector[]) defaultq) [ ostate.intValue() ]); + if( defaultq( ostate ) != null ) + idefault.addAll( defaultq( ostate ) ); + } + + if( reloc ) { + val m = 1 + this.nstates; + val _finals = new TreeMap(); + val _deltaq = new Array[HashMap]( m ); + val _defaultq = new Array[Vector]( m ); + var _qbinders: Array[Vector] = null; + + if( qbinders != null ) + _qbinders = new Array[Vector]( m ); + + relocate( 1, _finals, _deltaq, _defaultq, _qbinders ); + + this.nstates = m; + this.finals = _finals; + this._deltaq = _deltaq; + this._defaultq = _defaultq; + this.qbinders = _qbinders; + } + + this._deltaq ( 0 ) = idelta; + //System.out.println("normalize:deltaq[ 0 ]"+ idelta ); + this._defaultq( 0 ) = new Vector( idefault ); + + //System.out.println("normalize:defaultq[ 0 ]"+ idefault ); + + this.initials = new TreeSet(); + this.initials.add( q0 ); + } + + + /** called from Berry-Sethi construction. + */ + + def this(nstates:int, _labels:HashSet, initials: TreeSet, finals:TreeMap, deltaq:Array[HashMap], defaultq:Array[Vector], qbinders:Object ) = { + this(); + //Console.println("NWA::this(. . . . )"); + this.nstates = nstates; + this.labels = _labels; + this.initials = initials; + this.finals = finals; + this._deltaq = deltaq; + this._defaultq = defaultq; + this.qbinders = qbinders.asInstanceOf[Array[Vector]]; + //print(); + //System.exit(0); + } + + + + var offset:Array[int] = _; // only used if constructed from multiple + + def collectLabels( nfa:Array[NondetWordAutom ] ): Unit = { + this.labels = new HashSet(); + var i = 0; + while(i < nfa.length) { + this.labels.addAll( nfa( i ).labels ); + i = i + 1 + } + } + + def collectOffsets( nfa:Array[NondetWordAutom] ): Unit = { + this.offset = new Array[int]( nfa.length + 1 ); + offset( 0 ) = 1; // we have a new initial state + var i = 0; + while(i < nfa.length ) { + offset( i + 1 ) = nfa( i ).nstates + offset( i ); + i = i + 1 + } + } + + /** collapses several normalized NondetWordAutom objects into one. + */ + + def this( nfa: Array[NondetWordAutom] ) = { + this(); + //Console.println("NWA::this(.)"); + + //this.m + val m = nfa.length; + //System.out.println("enter NondetWordSwitch, " + // +"combining " + m + " automata"); + + collectLabels( nfa ); + collectOffsets( nfa ); + + //Console.println(" X 1"); + + + this.nstates = offset( nfa.length ); //m - 1 ] + nfa[ m - 1 ].nstates; + + + this.finals = new TreeMap(); + + this.qbinders = new Array[Vector]( nstates ); + + // new initial state gets all transitions from all initial states + + this._deltaq = new Array[HashMap]( nstates ); + this._defaultq = new Array[Vector]( nstates ); + //Console.println(" X 2"); + + //TreeSet defaultqSet = new TreeSet(); + _deltaq( 0 ) = new HashMap(); // 0 = our new initial state + + val initials = new TreeSet(); + + var i = 0; + while(i < m) { + //System.out.println("i (current NFA):"+i); + + val n = nfa( i ); + + val offs = offset( i ); + + initials.add( new Integer( offs )); + + n.relocate( offs, + this.finals, + this._deltaq, + this._defaultq, + this.qbinders ); + i = i + 1; + } + + normalize( initials, false ); + //Console.println("Leave-NWA::this(.)"); + } + + + + + def print(): Unit = { + + Console.print("NFA on labels "+ this.labels); + + if( offset != null ) { + Console.print("offset"); + var k = 0; + while(k < offset.length) { + if( k > 0) + Console.print(", "); + Console.print(offset(k)); + k = k + 1; + } + } + Console.println; + + Console.print("max state number :" + (nstates - 1) ); + + Console.println("initials" + initials); + + Console.println("finals" + finals); + + var i = 0; + while(i < nstates) { + Console.print("state: " + i); + if( finals.containsKey( new Integer( i )) ){ + Console.print("*"); //final + } + Console.print(" transitions: {"); + var arrows:HashMap = deltaq( i ); + + var it = arrows.keySet().iterator(); + while(it.hasNext()) { + val label = it.next(); + val targets = arrows.get( label ).asInstanceOf[Vector]; + val jt = targets.iterator(); + while(jt.hasNext()) { + val p = jt.next().asInstanceOf[Integer]; + Console.print("("+label+","+p+")"); + } + } + + Console.print("} "); + Console.print(" default transitions: "+_defaultq( i )); + if( null != qbinders ) + Console.println(" binders "+qbinders( i )); + Console.println; + i = i + 1; + } + } + + +} + +} diff --git a/src/compiler/scala/tools/nsc/matching/Npair.scala b/src/compiler/scala/tools/nsc/matching/Npair.scala new file mode 100644 index 0000000000..8cb050de2e --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/Npair.scala @@ -0,0 +1,64 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ +package scala.tools.nsc.matching ; + +import java.util.{ HashMap, TreeSet }; +/** cartesian + */ + +/** Int x TreeSet[ Int ] + */ +case class Npair(nstate: Integer, nset: TreeSet) { + + override def equals(that: Any): Boolean = { + this match { + case Npair(nstate, nset) => + that match { + case Npair(_nstate, _nset) => + return ((nstate == _nstate) + && (nset == _nset)); + case _ => return false + } + case _ => return false + } + } + + override def toString(): String = this match { + case Npair(nstate, nset) => + //Integer dstate = (Integer) indexMap.get(nset); + "<n" + nstate.toString() + " in " + nset /*+" = d"+dstate*/ + ">"; + case _ => null + } + + def toString(indexMap: HashMap): String = { + //assert indexMap != null; + this match { + case Npair(nstate, nset) => + //assert nstate != null; + val dstate = indexMap.get( nset ).asInstanceOf[Integer]; + return "<n" + nstate.toString() + " in " + nset + " = d" + dstate + ">"; + case _ => + return null; + } + } + + +} + +class NpairComparator extends StateSetComparator { + override def compare(o1: Any, o2: Any): Int = { + o1 match { + case Npair(nstate, nset) => o2 match { + case Npair(_nstate, _nset) => + val res = nstate.compareTo(_nstate); + if (res != 0) + return res; + else + return super.compare(nset, _nset); + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala b/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala new file mode 100644 index 0000000000..7d4292e3ff --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala @@ -0,0 +1,992 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ + +package scala.tools.nsc.matching ; + +import scala.tools.nsc.util.Position; + +trait PatternMatchers: (TransMatcher with PatternNodes) extends AnyRef with PatternNodeCreator { + + + import global._; + import typer.typed ; + import symtab.Flags; + + class PatternMatcher { + + + + protected var optimize = true; + protected var delegateSequenceMatching = false; + protected var doBinding = true; + + /** the owner of the pattern matching expression + */ + var owner:Symbol = _ ; + + /** the selector expression + */ + protected var selector: Tree = _; + + /** the root of the pattern node structure + */ + protected var root: PatternNode = _; + + /** the symbol of the result variable + */ +// protected var resultVar: Symbol = _; + + def defs = definitions; + /** init method, also needed in subclass AlgebraicMatcher + */ + def initialize(selector: Tree, owner: Symbol, doBinding: Boolean): Unit = { + + //Console.println("pm.initialize selector.tpe = "+selector.tpe); + + /* + this.mk = new PatternNodeCreator { + val global = PatternMatcher.this.global; + val unit = PatternMatcher.this.unit; + val owner = PatternMatcher.this.owner; + } + */ + /* + this.cf = new CodeFactory { + val global = PatternMatcher.this.global; + val unit = PatternMatcher.this.unit; + val owner = PatternMatcher.this.owner; + } + */ + this.root = pConstrPat(selector.pos, selector.tpe.widen); + //Console.println("selector.tpe "+selector.tpe); + //Console.println("selector.tpe.widen "+selector.tpe.widen); + //Console.println("root.symbol "+root.symbol); + //Console.println("root.symbol.tpe "+root.symbol.tpe); + this.root.and = pHeader(selector.pos, + selector.tpe.widen, + Ident(root.symbol).setType(root.tpe)); + //Console.println("resultType = "+resultType); + this.owner = owner; + this.selector = selector; + + this.optimize = this.optimize && (settings.target.value == "jvm"); + this.doBinding = doBinding; + } + + /** pretty printer + */ + def print(): Unit = { Console.println ( + root.and.print("", new StringBuffer()).toString() + )} + + /** enters a sequence of cases into the pattern matcher + */ + def construct(cases: List[Tree]): Unit = { + cases foreach enter; + } + + /** enter a single case into the pattern matcher + */ + protected def enter(caseDef: Tree): Unit = { + caseDef match { + case CaseDef(pat, guard, body) => + val env = new CaseEnv; + // PatternNode matched = match(pat, root); + val target = enter1(pat, -1, root, root.symbol, env); + // if (target.and != null) + // unit.error(pat.pos, "duplicate case"); + if (null == target.and) + target.and = pBody(caseDef.pos, env.getBoundVars(), guard, body); + else if (target.and.isInstanceOf[Body]) + updateBody(target.and.asInstanceOf[Body], env.getBoundVars(), guard, body); + else + cunit.error(pat.pos, "duplicate case"); + } + } + + protected def updateBody(tree: Body, bound: Array[ValDef], guard: Tree , body: Tree): Unit = { + if (tree.guard(tree.guard.length - 1) == EmptyTree) { + //unit.error(body.pos, "unreachable code"); + } else { + val bd = new Array[Array[ValDef]](tree.bound.length + 1); + val ng = new Array[Tree](tree.guard.length + 1); + val nb = new Array[Tree](tree.body.length + 1); + System.arraycopy(tree.bound, 0, bd, 0, tree.bound.length); + System.arraycopy(tree.guard, 0, ng, 0, tree.guard.length); + System.arraycopy(tree.body, 0, nb, 0, tree.body.length); + bd(bd.length - 1) = bound; + ng(ng.length - 1) = guard; + nb(nb.length - 1) = body; + tree.bound = bd ; + tree.guard = ng ; + tree.body = nb ; + } + } + + protected def patternArgs(tree: Tree):List[Tree] = { + tree match { + case Bind(_, pat) => + patternArgs(pat); + case Apply(_, args) => + if ( isSeqApply(tree.asInstanceOf[Apply]) && !delegateSequenceMatching) + args(0) match { + case ArrayValue(_, ts) => // test array values + ts; + //case Sequence(ts) => + // ts; + case _ => + args; + } + else args + case Sequence(ts) if (!delegateSequenceMatching) => + ts; + case ArrayValue(_, ts) => // test array values + ts; + case _ => + List(); + } + } + + /** returns true if apply is a "sequence apply". analyzer inserts Sequence nodes if something is a + * + * - last update: discussion with Martin 2005-02-18 + * + * - if true, tree.fn must be ignored. The analyzer ensures that the selector will be a subtype + * of fn; it thus assigns the expected type from the context (which is surely a subtype, + * but may have different flags etc. + * + * - so should be + * (( tree.args.length == 1 ) && tree.args(0).isInstanceOf[Sequence]) + * but fails + */ + protected def isSeqApply( tree: Apply ): Boolean = { + // Console.print("isSeqApply? "+tree.toString()); + // val res = + tree match { + case Apply(_, List(ArrayValue(_,_))) => (tree.tpe.symbol.flags & Flags.CASE) == 0 + case _ => false; + } + //Console.println(res); + //res; + } + + protected def patternNode(tree:Tree , header:Header , env: CaseEnv ): PatternNode = { + //if(tree!=null) Console.println("patternNode("+tree+","+header+")"); + //else scala.Predef.error("got null tree in patternNode"); + //Console.println("tree.tpe "+tree.tpe); + //Console.println("tree.getClass() "+tree.getClass()); + tree match { + case Bind(name, Typed(Ident( nme.WILDCARD ), tpe)) => // x@_:Type + if (isSubType(header.getTpe(),tpe.tpe)) { + //Console.println("U"); + val node = pDefaultPat(tree.pos, tpe.tpe); + env.newBoundVar( tree.symbol, tree.tpe, header.selector ); + node; + } else { + //Console.println("X"); + val node = pConstrPat(tree.pos, tpe.tpe); + env.newBoundVar( tree.symbol, + tpe.tpe /*scalac: tree.tpe */, + typed(Ident( node.casted ))); + node; + } + + case Bind(name, Ident(nme.WILDCARD)) => // x @ _ + val node = pDefaultPat(tree.pos, header.getTpe()); + if ((env != null) && (tree.symbol != defs.PatternWildcard)) + env.newBoundVar( tree.symbol, tree.tpe, header.selector); + node; + + case Bind(name, pat) => + val node = patternNode(pat, header, env); + if ((env != null) && (tree.symbol != defs.PatternWildcard)) { + val casted = node.symbol; + val theValue = if (casted == NoSymbol) header.selector else Ident( casted).setType(casted.tpe); + env.newBoundVar(tree.symbol, tree.tpe, theValue); + } + node; + + case t @ Apply(fn, args) => // pattern with args + //Console.println("Apply!"); + //Console.println("isSeqApply "+isSeqApply(t)); + //Console.println("delegateSequenceMatching "+delegateSequenceMatching); + if (isSeqApply(t)) { + if (!delegateSequenceMatching) { + args(0) match { + // case Sequence(ts)=> + case ArrayValue(_, ts)=> + //Console.println("doing pSeqpat "); + val res = pSequencePat(tree.pos, tree.tpe, ts.length); + //Console.println("pSeqpat.casted = "+res.casted); + //Console.println("pSeqpat.casted.pos = "+res.casted.pos); + res + } + } else { + //Console.println("delegating ... "); + val res = pConstrPat(tree.pos, tree.tpe); + res.and = pHeader(tree.pos, header.getTpe(), header.selector); + //res.and.and = pSeqContainerPat(tree.pos, tree.tpe, args(0)); + res.and.and = pSeqContainerPat(tree.pos, tree.tpe, Sequence(args(0).asInstanceOf[ArrayValue].elems)); + res; + } + } else if ((fn.symbol != null) && + fn.symbol.isStable && + !(fn.symbol.isModule && + ((fn.symbol.flags & Flags.CASE) != 0))) { + pVariablePat(tree.pos, tree); + } + else { + /* + Console.println("apply but not seqApply"); + Console.println("tree.tpe="+tree.tpe); + Console.println("tree.symbol="+tree.symbol); + */ + pConstrPat(tree.pos, tree.tpe); + } + case Typed(Ident( nme.WILDCARD ), tpe) => // x@_:Type + val doTest = isSubType(header.getTpe(),tpe.tpe); + if(doTest) + pDefaultPat(tree.pos, tpe.tpe) + else + pConstrPat(tree.pos, tpe.tpe); + + case t @ Typed(ident, tpe) => // variable pattern + //Console.println("Z"); + val doTest = isSubType(header.getTpe(),tpe.tpe); + val node = { + if(doTest) + pDefaultPat(tree.pos, tpe.tpe) + else + pConstrPat(tree.pos, tpe.tpe); + } + //if(t.expr.symbol == NoSymbol) { + // Console.println(t.toString()); + // scala.Predef.error("go typed pattern with no symbol in "+cunit.toString()); + // } + if ((null != env) /* && (ident.symbol != defs.PatternWildcard) */) + node match { + case ConstrPat(casted) => + env.newBoundVar(t.expr.symbol, + tpe.tpe, + Ident( casted ).setType(casted.tpe)); + case _ => + env.newBoundVar(t.expr.symbol, + tpe.tpe, + {if(doTest) + header.selector + else + typed(Ident(node + .asInstanceOf[ConstrPat] + .casted))}); + } + node; + + case Ident(nme.WILDCARD) => + pDefaultPat(tree.pos, header.getTpe()); + + case Ident(name) => // pattern without args or variable + + // nsc: wildcard's don't have symbols anymore! + //if (tree.symbol == defs.PatternWildcard) + // pDefaultPat(tree.pos, header.getTpe()); + //else + + if (tree.symbol.isPrimaryConstructor) { + scala.Predef.error("error may not happen: ident is primary constructor"+tree.symbol); // Burak + + } else if (treeInfo.isVariableName(name)) {// Burak + //old scalac + scala.Predef.error("this may not happen"); // Burak + + //nsc: desugarize (in case nsc does not do it) + /* + Console.println("Ident("+name+") in unit"+cunit); + Console.println("tree.symbol = "+tree.symbol); + // = treat the same as Bind(name, _) + val node = pDefaultPat(tree.pos, header.getTpe()); + if ((env != null) && (tree.symbol != defs.PatternWildcard)) + env.newBoundVar( tree.symbol, tree.tpe, header.selector); + node; + */ + } else + pVariablePat(tree.pos, tree); // a named constant Foo + + case Select(_, name) => // variable + if (tree.symbol.isPrimaryConstructor) + pConstrPat(tree.pos, tree.tpe); + else + pVariablePat(tree.pos, tree); + + case Literal(Constant(value)) => + pConstantPat(tree.pos, tree.tpe, value); + + //case Sequence(ts) => + case ArrayValue(_, ts) => + if ( !delegateSequenceMatching ) { + pSequencePat(tree.pos, tree.tpe, ts.length); + } else { + pSeqContainerPat(tree.pos, tree.tpe, tree); + } + case Alternative(ts) => + if(ts.length < 2) + scala.Predef.error("ill-formed Alternative"); + val subroot = pConstrPat(header.pos, header.getTpe()); + subroot.and = pHeader(header.pos, header.getTpe(), header.selector.duplicate); + val subenv = new CaseEnv; + var i = 0; while(i < ts.length) { + val target = enter1(ts(i), -1, subroot, subroot.symbol, subenv); + target.and = pBody(tree.pos); + i = i + 1 + } + pAltPat(tree.pos, subroot.and.asInstanceOf[Header]); + + case _ => + if(tree == null) + scala.Predef.error("unit = " + cunit + "; tree = null"); + else + scala.Predef.error("unit = " + cunit + "; tree = "+tree); + } + } + + protected def enter(pat: Tree, index: Int, target: PatternNode, casted: Symbol, env: CaseEnv ): PatternNode = { + target match { + case ConstrPat(newCasted) => + enter1(pat, index, target, newCasted, env); + case SequencePat(newCasted, len) => + enter1(pat, index, target, newCasted, env); + case _ => + enter1(pat, index, target, casted, env); + } + } + + private def newHeader(pos: Int, casted: Symbol, index: Int): Header = { + //Console.println("newHeader(pos,"+casted+","+index+")"); + //Console.println(" casted.tpe"+casted.tpe); + //Console.println(" casted.pos "+casted.pos+" equals firstpos?"+(casted.pos == Position.FIRSTPOS)); + val ident = typed(Ident(casted)); + if (casted.pos == Position.FIRSTPOS) { + //Console.println("FIRSTPOS"); + + //Console.println("DEBUG"); + //Console.println(); + + val t = typed( + Apply(Select( ident, ident.tpe.member(nme.apply)/* scalac: defs.functionApply( 1 )*/), + List( Literal( Constant(index) ) ))); + val seqType = t.tpe; + pHeader( pos, seqType, t ); + } else { + //Console.println("NOT FIRSTPOS"); + // Console.println("newHeader :: casted="+casted); + // Console.println("newHeader :: casted.tpe="+casted.tpe); + //Console.println("newHeader :: "); + val caseAccs = casted.tpe.symbol.caseFieldAccessors; + if (caseAccs.length <= index) System.out.println("selecting " + index + " in case fields of " + casted.tpe.symbol + "=" + casted.tpe.symbol.caseFieldAccessors);//debug + val ts = caseAccs(index); + //Console.println("newHeader :: ts="+ts); + //val accType = casted.tpe.memberType(ts); // old scalac + //val accTree = global.typer.typed(Select(ident, ts)); // ! + + val accTree = typed(Apply(Select(ident, ts), List())); // nsc ! + val accType = accTree.tpe; + //Console.println("newHeader :: accType="+accType); + //Console.println("newHeader :: accType.resultType ="+accType.resultType); + //Console.println("accTree.tpe =="+accTree.tpe); + + accType match { + // scala case accessor + case MethodType(_, _) => + //Console.println("Hello?!"); + pHeader(pos, accType.resultType, Apply(accTree, List())); + // jaco case accessor + case _ => + //Console.println("Hola?!"); + pHeader(pos, accType, accTree); + } + } + } + + /** main enter function + * + * invariant: ( curHeader == (Header)target.and ) holds + */ + protected def enter1(pat: Tree, index: Int, target: PatternNode, casted: Symbol, env: CaseEnv): PatternNode = { + //System.err.println("enter(" + pat + ", " + index + ", " + target + ", " + casted + ")"); + val patArgs = patternArgs(pat); // get pattern arguments + var curHeader = target.and.asInstanceOf[Header]; // advance one step in intermediate representation + if (curHeader == null) { // check if we have to add a new header + //assert index >= 0 : casted; + if (index < 0) { scala.Predef.error("error entering:" + casted); return null } + target.and = {curHeader = newHeader(pat.pos, casted, index); curHeader}; + curHeader.or = patternNode(pat, curHeader, env); + enter(patArgs, curHeader.or, casted, env); + } + else { + // find most recent header + while (curHeader.next != null) + curHeader = curHeader.next; + // create node + var patNode = patternNode(pat, curHeader, env); + var next: PatternNode = curHeader; + // add branch to curHeader, but reuse tests if possible + while (true) { + if (next.isSameAs(patNode)) { // test for patNode already present --> reuse + // substitute... !!! + patNode match { + case ConstrPat(ocasted) => + env.substitute(ocasted, typed(Ident(next.asInstanceOf[ConstrPat].casted))); + case _ => + } + return enter(patArgs, next, casted, env); + } else if (next.isDefaultPat() || // default case reached, or + ((next.or == null) && // no more alternatives and + (patNode.isDefaultPat() || next.subsumes(patNode)))) { + // new node is default or subsumed + var header = pHeader(patNode.pos, + curHeader.getTpe(), + curHeader.selector); + {curHeader.next = header; header}; + header.or = patNode; + return enter(patArgs, + patNode, + casted, + env); + } + else if (next.or == null) { + return enter(patArgs, {next.or = patNode; patNode}, casted, env); // add new branch + } else + next = next.or; + } + error("must not happen"); + null + } + } + + /** calls enter for an array of patterns, see enter + */ + protected def enter(pats:List[Tree], target1: PatternNode , casted1: Symbol , env: CaseEnv): PatternNode = { + var target = target1; + var casted = casted1; + target match { + case ConstrPat(newCasted) => + casted = newCasted; + case SequencePat(newCasted, len) => + casted = newCasted; + case _ => + } + var i = 0; while(i < pats.length) { + target = enter1(pats(i), i, target, casted, env); + i = i + 1 + } + target; + } + + protected def nCaseComponents(tree: Tree): int = { + tree match { + case Apply(fn, _) => + val tpe = tree.tpe.symbol.primaryConstructor.tpe; + //Console.println("~~~ " + tree.type() + ", " + tree.type().symbol.primaryConstructor()); + tpe match { + // I'm not sure if this is a good idea, but obviously, currently all case classes + // without constructor arguments have type NoType + case NoType => + error("this cannot happen"); + 0 + case MethodType(args, _) => + args.length; + case PolyType(tvars, MethodType(args, _)) => + args.length; + case PolyType(tvars, _) => + 0; + case _ => + error("not yet implemented;" + + "pattern matching for " + tree + ": " + tpe); + } + } + return 0; + } + + + //////////// generator methods + + def toTree(): Tree = { + if (optimize && isSimpleIntSwitch()) + intSwitchToTree(); + + else /* if (false && optimize && isSimpleSwitch()) + switchToTree(); + else */ { + //print(); + generalSwitchToTree(); + } + } + + case class Break(res:Boolean) extends java.lang.Throwable; + case class Break2() extends java.lang.Throwable; + + // TODO disentangle this + protected def isSimpleSwitch(): Boolean = { + print(); + var patNode = root.and; + while (patNode != null) { + var node = patNode; + while (({node = node.or; node}) != null) { + node match { + case VariablePat(tree) => + Console.println(((tree.symbol.flags & Flags.CASE) != 0)); + case ConstrPat(_) => + Console.println(node.getTpe().toString() + " / " + ((node.getTpe().symbol.flags & Flags.CASE) != 0)); + var inner = node.and; + def funct(inner: PatternNode): Boolean = { + //outer: while (true) { + inner match { + case _h:Header => + if (_h.next != null) + throw Break(false); + funct(inner.or) + + case DefaultPat() => + funct(inner.and); + + case b:Body => + if ((b.guard.length > 1) || + (b.guard(0) != EmptyTree)) + throw Break(false); + + throw Break2() // break outer + case _ => + Console.println(inner); + throw Break(false); + } + } + var res = false; + var set = false; + try { + funct(inner) + } catch { + case ex: Break => + res = ex.res; + set = true; + case ex: Break2 => + } + if(set) return res; + case _ => + return false; + } + } + patNode = patNode.nextH(); + } + return true; + } + + protected def isSimpleIntSwitch(): Boolean = { + if (isSameType(selector.tpe.widen, defs.IntClass.tpe)) { + var patNode = root.and; + while (patNode != null) { + var node = patNode; + while (({node = node.or; node}) != null) { + node match { + case ConstantPat(_) => ; + case _ => + return false; + } + node.and match { + case _b:Body => + if ((_b.guard.length > 1) || + (_b.guard(0) != EmptyTree) || + (_b.bound(0).length > 0)) + return false; + case _ => + return false; + } + } + patNode = patNode.nextH(); + } + return true; + } else + return false; + } + + class TagBodyPair(tag1: Int, body1: Tree, next1:TagBodyPair ) { + + var tag: int = tag1; + var body: Tree = body1; + var next: TagBodyPair = next1; + + def length(): Int = { + if (null == next) 1 else (next.length() + 1); + } + } + + protected def numCases(patNode1: PatternNode): Int = { + var patNode = patNode1; + var n = 0; + while (({patNode = patNode.or; patNode}) != null) + patNode match { + case DefaultPat() => ; + case _ => + n = n + 1; + } + n; + } + + protected def defaultBody(patNode1: PatternNode, otherwise: Tree ): Tree = { + var patNode = patNode1; + while (patNode != null) { + var node = patNode; + while (({node = node.or; node}) != null) + node match { + case DefaultPat() => + return node.and.bodyToTree(); + case _ => + } + patNode = patNode.nextH(); + } + otherwise; + } + + /** This method translates pattern matching expressions that match + * on integers on the top level. + */ + def intSwitchToTree(): Tree = { + def insert1(tag: Int, body: Tree, current: TagBodyPair): TagBodyPair = { + if (current == null) + return new TagBodyPair(tag, body, null); + else if (tag > current.tag) + return new TagBodyPair(current.tag, current.body, insert1(tag, body, current.next)); + else + return new TagBodyPair(tag, body, current); + } + + //print(); + val ncases = numCases(root.and); + val matchError = ThrowMatchError(selector.pos, resultType); + // without a case, we return a match error if there is no default case + if (ncases == 0) + return defaultBody(root.and, matchError); + // for one case we use a normal if-then-else instruction + else if (ncases == 1) { + root.and.or match { + case ConstantPat(value) => + return If(Equals(selector, Literal(value)), + (root.and.or.and).bodyToTree(), + defaultBody(root.and, matchError)); + case _ => + return generalSwitchToTree(); + } + } + // + // if we have more than 2 cases than use a switch statement + val _h:Header = root.and.asInstanceOf[Header]; + + val next = _h.next; + var mappings: TagBodyPair = null; + var defaultBody1: Tree = matchError; + var patNode = root.and; + while (patNode != null) { + var node = patNode.or; + while (node != null) { + node match { + case DefaultPat() => + if (defaultBody1 != null) + scala.Predef.error("not your day today"); + defaultBody1 = node.and.bodyToTree(); + node = node.or; + + case ConstantPat( value: Int )=> + mappings = insert1( + value, + node.and.bodyToTree(), + mappings); + node = node.or; + } + } + patNode = patNode.nextH(); + } + + var n = mappings.length(); + var nCases: List[CaseDef] = Nil; + while (mappings != null) { + nCases = CaseDef(Literal(mappings.tag), + mappings.body) :: nCases; + mappings = mappings.next; + } + /* + val tags = new Array[Int](n); + val bodies = new Array[Tree](n); + n = 0; + while (mappings != null) { + tags(n) = mappings.tag; + bodies(n) = mappings.body; + n = n + 1; + mappings = mappings.next; + } + return Switch(selector, tags, bodies, defaultBody1, resultType); + */ + nCases = CaseDef(Ident(nme.WILDCARD), defaultBody1) :: nCases; + return Match(selector, nCases) + } + + + var exit:Symbol = null; + /** simple optimization: if the last pattern is `case _' (no guards), we won't generate the ThrowMatchError + */ + def generalSwitchToTree(): Tree = { + this.exit = currentOwner.newLabel(root.pos, "exit") + .setInfo(new MethodType(List(resultType), resultType)); + //Console.println("resultType:"+resultType.toString()); + val result = exit.newValueParameter(root.pos, "result").setInfo( resultType ); + + //Console.println("generalSwitchToTree: "+root.or); + /* + val ts = List(ValDef(root.symbol, selector)); + val res = If(toTree(root.and), + LabelDef(exit, List(result), Ident(result)), + ThrowMatchError(selector.pos, resultType // , Ident(root.symbol) + )); + return Block(ts, res); + */ + return Block( + List( + ValDef(root.symbol, selector), + toTree(root.and), + ThrowMatchError(selector.pos, resultType)), + LabelDef(exit, List(result), Ident(result))) + } + + /*protected*/ def toTree(node1: PatternNode): Tree = { + def optimize1(selType:Type, alternatives1: PatternNode ): Boolean = { + var alts = alternatives1; + if (!optimize || !isSubType(selType, defs.ScalaObjectClass.tpe)) + return false; + var cases = 0; + while (alts != null) { + alts match { + case ConstrPat(_) => + if (alts.getTpe().symbol.hasFlag(Flags.CASE)) + cases = cases +1; + else + return false; + + case DefaultPat() => + ; + case _ => + return false; + } + alts = alts.or; + } + return cases > 2; + } // def optimize + + var node = node1; + + var res: Tree = typed(Literal(Constant(false))); //.setInfo(defs.BooleanClass); + //Console.println("pm.toTree res.tpe "+res.tpe); + while (node != null) + node match { + case _h:Header => + val selector = _h.selector; + val next = _h.next; + //res = And(mkNegate(res), toTree(node.or, selector)); + //Console.println("HEADER TYPE = " + selector.type); + if (optimize1(node.getTpe(), node.or)) + res = Or(res, toOptTree(node.or, selector)); + else + res = Or(res, toTree(node.or, selector)); + node = next; + + case _b:Body => + var bound = _b.bound; + val guard = _b.guard; + val body = _b.body; + if ((bound.length == 0) && + (guard.length == 0) && + (body.length == 0)) { + return Literal(Constant(true)); + } else if (!doBinding) + bound = Predef.Array[Array[ValDef]]( Predef.Array[ValDef]() ); + var i = guard.length - 1; while(i >= 0) { + val ts:Seq[Tree] = bound(i).asInstanceOf[Array[Tree]]; + val temp = currentOwner.newValue(body(i).pos, cunit.fresh.newName("r$")) + .setFlag(Flags.SYNTHETIC).setInfo(resultType); + var res0: Tree = + //Block( + // List(Assign(Ident(resultVar), body(i))), + // Literal(Constant(true))); + Block( + List( + ValDef(temp, body(i)), + Apply(Ident(exit), List(Ident(temp)))), + Literal(Constant(true)) + ); // forward jump + if (guard(i) != EmptyTree) + res0 = And(guard(i), res0); + res = Or(Block(ts.toList, res0), res); + i = i - 1 + } + return res; + case _ => + scala.Predef.error("I am tired"); + } + return res; + } + + + class TagNodePair(tag1: int, node1: PatternNode, next1: TagNodePair) { + var tag: int = tag1; + var node: PatternNode = node1; + var next: TagNodePair = next1; + + def length(): Int = { + return if (null == next) 1 else (next.length() + 1); + } + } + + protected def toOptTree(node1: PatternNode, selector: Tree): Tree = { + def insert2(tag: Int, node: PatternNode, current: TagNodePair): TagNodePair = { + if (current == null) + return new TagNodePair(tag, node, null); + else if (tag > current.tag) + return new TagNodePair(current.tag, current.node, insert2(tag, node, current.next)); + else if (tag == current.tag) { + val old = current.node; + ({current.node = node; node}).or = old; + return current; + } else + return new TagNodePair(tag, node, current); + } + + def insertNode(tag:int , node:PatternNode , current:TagNodePair ): TagNodePair = { + val newnode = node.dup(); + newnode.or = null; + return insert2(tag, newnode, current); + } + var node = node1; + //System.err.println("pm.toOptTree called"+node); + var cases: TagNodePair = null; + var defaultCase: PatternNode = null; + while (node != null) + node match { + case ConstrPat(casted) => + cases = insertNode(node.getTpe().symbol.tag, node, cases); + node = node.or; + + case DefaultPat() => + defaultCase = node; + node = node.or; + + case _ => + scala.Predef.error("errare humanum est"); + } + var n = cases.length(); + /* + val tags = new Array[int](n); + val bodies = new Array[Tree](n); + n = 0; + while (null != cases) { + tags(n) = cases.tag; + bodies(n) = toTree(cases.node, selector); + n = n + 1; + cases = cases.next; + } + */ + + + /* + return + Switch( + Apply( + Select(selector.duplicate, defs.ScalaObjectClass_tag), + List()), + tags, + bodies, + { if (defaultCase == null) Literal(false) else toTree(defaultCase.and) }, + defs.boolean_TYPE()); + */ + var nCases: List[CaseDef] = Nil; + while (cases != null) { + nCases = CaseDef(Literal(Constant(cases.tag)), + toTree(cases.node, selector)) :: nCases; + cases = cases.next; + } + + val defBody = if (defaultCase == null) + Literal(Constant(false)) + else + toTree(defaultCase.and); + + nCases = CaseDef(Ident(nme.WILDCARD), defBody) :: nCases; + return Match(Apply(Select(selector.duplicate, defs.ScalaObjectClass_tag), + List()), + nCases); + } + + protected def toTree(node:PatternNode , selector:Tree ): Tree = { + //Console.println("pm.toTree("+node+","+selector+")"); + //Console.println("pm.toTree selector.tpe = "+selector.tpe+")"); + if(selector.tpe == null) + scala.Predef.error("cannot go on"); + if (node == null) + return Literal(Constant(false)); + else + node match { + case DefaultPat() => + return toTree(node.and); + + case ConstrPat(casted) => + return If(gen.mkIsInstanceOf(selector.duplicate, node.getTpe()), + Block( + List(ValDef(casted, + gen.mkAsInstanceOf(selector.duplicate, node.getTpe(), true))), + toTree(node.and)), + toTree(node.or, selector.duplicate)); + case SequencePat(casted, len) => + return ( + Or( + And( + And(gen.mkIsInstanceOf(selector.duplicate, node.getTpe()), + Equals( + typed( + Apply( + Select( + gen.mkAsInstanceOf(selector.duplicate, + node.getTpe(), + true), + node.getTpe().member(nme.length) /*defs.Seq_length*/), + List()) + ), + typed( + Literal(Constant(len)) + ))), + Block( + List( + ValDef(casted, + gen.mkAsInstanceOf(selector.duplicate, node.getTpe(), true))), + toTree(node.and))), + toTree(node.or, selector.duplicate))); + case ConstantPat(value) => + //Console.println("selector = "+selector); + //Console.println("selector.tpe = "+selector.tpe); + return If(Equals(selector.duplicate, + typed(Literal(Constant(value))).setType(node.tpe)), + toTree(node.and), + toTree(node.or, selector.duplicate)); + case VariablePat(tree) => + return If(Equals(selector.duplicate, tree), + toTree(node.and), + toTree(node.or, selector.duplicate)); + case AltPat(header) => + return If(toTree(header), + toTree(node.and), + toTree(node.or, selector.duplicate)); + case _ => + scala.Predef.error("can't plant this tree"); + } + } +} + + +} diff --git a/src/compiler/scala/tools/nsc/matching/PatternNodeCreator.scala b/src/compiler/scala/tools/nsc/matching/PatternNodeCreator.scala new file mode 100644 index 0000000000..79ba780469 --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/PatternNodeCreator.scala @@ -0,0 +1,108 @@ +package scala.tools.nsc.matching; + +import scala.tools.nsc.util.Position; + +/** PatternNode factory. + * we inherit the globals from PatternTool. + */ + +trait PatternNodeCreator: (TransMatcher with PatternNodes) { + + import global._; + + def pSequencePat(pos: Int , tpe:Type , len:int) = { + //assert (tpe != null); + val sym = newVar(Position.FIRSTPOS, tpe); + //Console.println("pncrea::sequencePat sym.pos = "+sym.pos); + val node = new SequencePat(sym, len); + node.pos = pos; + node.tpe = tpe; + //Console.println("pncrea::sequencePat sym.pos = "+sym.pos); + node; + } + + def pSeqContainerPat(pos: int, tpe: Type, seqpat:Tree ) = { + //assert (tpe != null); + val sym = newVar(Position.NOPOS, tpe); + val node = new SeqContainerPat(sym, seqpat); + node.pos = pos; + node.setType(tpe); + node; + } + + def pDefaultPat(pos: int, tpe: Type) = { + //assert (tpe != null); + val node = new DefaultPat(); + node.pos = pos; + node.setType(tpe); + node; + } + + def pConstrPat(pos: int, tpe: Type) = { + //assert (tpe != null); + val node = new ConstrPat(newVar(pos, tpe)); + node.pos = pos; + node.setType(tpe); + node; + } + + def pConstantPat(pos: int, tpe: Type, value: Any /*AConstant*/ ) = { + //assert (tpe != null); + val node = new ConstantPat( value ); + node.pos = pos; + node.setType(tpe); + node; + } + + def pVariablePat(pos: int, tree:Tree) = { + //assert (tree.tpe != null); + val node = new VariablePat( tree ); + node.pos = pos; + node.setType(tree.tpe); + node; + } + + def pAltPat(pos: int, header:Header ) = { + val node = new AltPat(header); + node.pos = pos; + node.setType(header.getTpe()); + node; + } + + // factories + + def pHeader(pos: int, tpe: Type, selector:Tree) = { + //assert (tpe != null); + val node = new Header(selector, null); + node.pos = pos; + node.setType(tpe); + node; + } + + def pBody(pos: int) = { + val node = new Body(new Array[Array[ValDef]](0), new Array[Tree](0), new Array[Tree](0)); + node.pos = pos; + node; + } + + def pBody(pos: int, bound:Array[ValDef] , guard:Tree, body:Tree) = { + val node = new Body(Predef.Array[Array[ValDef]](bound), Predef.Array[Tree](guard), Predef.Array[Tree](body)); + node.pos = pos; + node; + } + + def newVar(pos: int, name: Name, tpe: Type): Symbol= { + /** hack: pos has special meaning*/ + val sym = currentOwner.newVariable(pos, name); + //Console.println("patnodcre::newVar sym = "+sym+ "tpe = "+tpe); + sym.setInfo(tpe); + //System.out.println("PatternNodeCreator::newVar creates symbol "+sym); + //System.out.println("owner: "+sym.owner()); + sym; + } + + def newVar(pos: int, tpe: Type): Symbol = { + newVar(pos, cunit.fresh.newName("temp"), tpe); + } +} + diff --git a/src/compiler/scala/tools/nsc/matching/PatternNodes.scala b/src/compiler/scala/tools/nsc/matching/PatternNodes.scala new file mode 100644 index 0000000000..551d964c39 --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/PatternNodes.scala @@ -0,0 +1,371 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scala.tools.nsc.matching ; + +import scala.tools.nsc.util.Position; + +trait PatternNodes: TransMatcher { + + import global._; + + /** Intermediate data structure for algebraic + pattern matcher + */ + class PatternNode { + var pos = Position.FIRSTPOS; + var tpe: Type = _; + var or: PatternNode = _; + var and: PatternNode = _; + + def bodyToTree(): Tree = this match { + case _b:Body => + return _b.body(0); + } + + def getTpe(): Type = { + tpe; + } + def setType(tpe: Type): Unit = { + this.tpe = tpe; + } + + def dup(): PatternNode = { + var res: PatternNode = null; + this match { + case h:Header => + res = new Header(h.selector, h.next); + + case b:Body=> + res = new Body(b.bound, b.guard, b.body); + + case DefaultPat() => + res = DefaultPat(); + + case ConstrPat(casted) => + res = ConstrPat(casted); + + case SequencePat(casted, len) => + res = SequencePat(casted, len); + + case SeqContainerPat(casted, seqpat) => + res = SeqContainerPat(casted, seqpat); + + case ConstantPat(value) => + res = ConstantPat(value); + + case VariablePat(tree) => + res = VariablePat(tree); + + case AltPat(subheader) => + res = AltPat(subheader); + + case _ => + error("") + } + res.pos = pos; + res.tpe = tpe; + res.or = or; + res.and = and; + res; + } + + def symbol: Symbol = { + this match { + case ConstrPat(casted) => + return casted; + case SequencePat(casted, _) => + return casted; + case SeqContainerPat(casted, _) => + return casted; + case _ => + return NoSymbol; //.NONE; + } + } + + def nextH(): PatternNode = { + this match { + case _h:Header => + return _h.next; + case _ => + return null; + } + } + + def isDefaultPat(): boolean = { + this match { + case DefaultPat() => + return true; + case _ => + return false; + } + } + + /** returns true if + * p and q are equal (constructor | sequence) type tests, or + * "q matches" => "p matches" + */ + def isSameAs(q: PatternNode): boolean = { + this match { + case ConstrPat(_) => + q match { + case ConstrPat(_) => + isSameType(q.getTpe(), this.getTpe()); + case _ => + false + } + case SequencePat(_, plen) => + q match { + case SequencePat(_, qlen) => + return (plen == qlen) && isSameType(q.getTpe(), this.getTpe()); + case _ => + false + } + case _ => + subsumes(q); + } + } + + /** returns true if "q matches" => "p matches" + */ + def subsumes(q:PatternNode): Boolean = { + this match { + case DefaultPat() => + q match { + case DefaultPat() => + true; + case _ => + false; + } + case ConstrPat(_) => + q match { + case ConstrPat(_) => + isSubType(q.getTpe(), this.getTpe()); + case _ => + false; + } + case SequencePat(_, plen) => + q match { + case SequencePat(_, qlen) => + (plen == qlen) && isSubType(q.getTpe(), this.getTpe()); + case _ => + false; + } + case ConstantPat(pval) => + q match { + case ConstantPat(qval) => + pval == qval; + case _ => + false; + } + case VariablePat(tree) => + q match { + case VariablePat(other) => + ((tree.symbol != null) && + (tree.symbol != NoSymbol) && + (!tree.symbol.isError) && + (tree.symbol == other.symbol)) + case _ => + false; + } + case _ => + false; + } + } + + override def toString(): String = { + this match { + case _h:Header => + return "Header(" + _h.selector + ")"; + case _b:Body => + return "Body"; + case DefaultPat() => + return "DefaultPat"; + case ConstrPat(casted) => + return "ConstrPat(" + casted + ")"; + case SequencePat(casted, len) => + return "SequencePat(" + casted + ", " + len + "...)"; + case SeqContainerPat(casted, seqpat) => + return "SeqContainerPat(" + casted + ", " + seqpat + ")"; + case ConstantPat(value) => + return "ConstantPat(" + value + ")"; + case VariablePat(tree) => + return "VariablePat"; + case _ => + return "<unknown pat>"; + } + } + + def print(indent: String, sb: StringBuffer): StringBuffer = { + + val patNode = this; + + def cont = if (patNode.or != null) patNode.or.print(indent, sb); else sb; + + def newIndent(s: String) = { + val removeBar: Boolean = (null == patNode.or); + val sb = new StringBuffer(); + sb.append(indent); + if (removeBar) + sb.setCharAt(indent.length() - 1, ' '); + var i = 0; while (i < s.length()) { + sb.append(' '); + i = i + 1 + } + sb.toString() + } + + if (patNode == null) + sb.append(indent).append("NULL"); + else + patNode match { + + case _h: Header => + val selector = _h.selector; + val next = _h.next; + sb.append(indent + "HEADER(" + patNode.getTpe() + + ", " + selector + ")").append('\n'); + patNode.or.print(indent + "|", sb); + if (next != null) + next.print(indent, sb); + else + sb + case ConstrPat(casted) => + val s = ("-- " + patNode.getTpe().symbol.name + + "(" + patNode.getTpe() + ", " + casted + ") -> "); + val nindent = newIndent(s); + sb.append(nindent + s).append('\n'); + patNode.and.print(nindent, sb); + cont; + + case SequencePat( casted, plen ) => + val s = ("-- " + patNode.getTpe().symbol.name + "(" + + patNode.getTpe() + + ", " + casted + ", " + plen + ") -> "); + val nindent = newIndent(s); + sb.append(indent + s).append('\n'); + patNode.and.print(nindent, sb); + cont; + + case DefaultPat() => + sb.append(indent + "-- _ -> ").append('\n'); + patNode.and.print(indent.substring(0, indent.length() - 1) + + " ", sb); + cont; + + case ConstantPat(value) => + val s = "-- CONST(" + value + ") -> "; + val nindent = newIndent(s); + sb.append(indent + s).append('\n'); + patNode.and.print( nindent, sb); + cont; + + case VariablePat(tree) => + val s = "-- STABLEID(" + tree + ": " + patNode.getTpe() + ") -> "; + val nindent = newIndent(s); + sb.append(indent + s).append('\n'); + patNode.and.print(nindent, sb); + cont; + + case AltPat(header) => + sb.append(indent + "-- ALTERNATIVES:").append('\n'); + header.print(indent + " * ", sb); + patNode.and.print(indent + " * -> ", sb); + cont; + + case _b:Body => + if ((_b.guard.length == 0) && (_b.body.length == 0)) + sb.append(indent + "true").append('\n') ; + else + sb.append(indent + "BODY(" + _b.body.length + ")").append('\n'); + + } + } // def print + + } + + class Header(sel1: Tree, next1: Header ) extends PatternNode { + var selector: Tree = sel1; + var next: Header = next1; + } + + class Body(bound1: Array[Array[ValDef]] , guard1:Array[Tree] , body1:Array[Tree] ) extends PatternNode { + var bound = bound1; + var guard = guard1; + var body = body1; + } + + case class DefaultPat()extends PatternNode; + case class ConstrPat(casted:Symbol ) extends PatternNode; + case class ConstantPat(value: Any /*AConstant*/ ) extends PatternNode; + case class VariablePat(tree: Tree ) extends PatternNode; + case class AltPat(subheader: Header ) extends PatternNode; + case class SequencePat( casted: Symbol, len:int) extends PatternNode; // only used in PatternMatcher + case class SeqContainerPat(casted: Symbol , seqpat: Tree ) extends PatternNode; // in AlgebraicMatcher + + /** the environment for a body of a case + * @param owner the owner of the variables created here + */ + class CaseEnv { +// (val owner:Symbol, unit:CompilationUnit) + private var boundVars:scala.Array[ValDef] = new Array[ValDef](4); + private var numVars = 0; + + /** substitutes a symbol on the right hand side of a ValDef + */ + def substitute(oldSym: Symbol, newInit: Tree): Unit = { + var i = 0; while( i < numVars) { + if( boundVars(i).rhs.symbol == oldSym ) { + boundVars(i) = ValDef(boundVars(i).symbol, newInit); + return; + } + i = i + 1; + } + } + + def newBoundVar(sym:Symbol, tpe: Type, init:Tree ): Unit = { + //if(sym == Symbol.NoSymbol ) { +// scala.Predef.Error("can't add variable with NoSymbol"); +// } + // sym.setOwner( owner ); // FIXME should be corrected earlier + // @maybe is corrected now? bq + if (numVars == boundVars.length) { + val newVars = new Array[ValDef](numVars * 2); + System.arraycopy(boundVars, 0, newVars, 0, numVars); + this.boundVars = newVars; + } + sym.setInfo(tpe); + this.boundVars(numVars) = ValDef(sym, init.duplicate); + numVars = numVars + 1; + } + + def getBoundVars(): Array[ValDef] = { + val newVars = new Array[ValDef](numVars); + System.arraycopy(boundVars, 0, newVars, 0, numVars); + return newVars; + } + + override def equals(obj: Any): Boolean = { + if (!(obj.isInstanceOf[CaseEnv])) + return false; + val env = obj.asInstanceOf[CaseEnv]; + if (env.numVars != numVars) + return false; + var i = 0; while(i < numVars) { + if ((boundVars(i).name != env.boundVars(i).name) || + !isSameType(boundVars(i).tpe, env.boundVars(i).tpe) || + (boundVars(i).rhs != env.boundVars(i).rhs)) + return false; + i = i + 1; + } + return true; + } + + } // class CaseEnv + + +} diff --git a/src/compiler/scala/tools/nsc/matching/RightTracers.scala b/src/compiler/scala/tools/nsc/matching/RightTracers.scala new file mode 100644 index 0000000000..f0daf20fed --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/RightTracers.scala @@ -0,0 +1,537 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ + +package scala.tools.nsc.matching; + +import java.util._ ; + +import scala.tools.nsc.util.Position; +import scala.tools.nsc.symtab.Flags; + +trait RightTracers: TransMatcher { + + import global._ ; + import java.util._ ; + +//import Scope.SymbolIterator; + +//import scalac.util.Name ; +//import scalac.util.Names ; + +//import scala.tools.util.Position; + + /** translate right tracer to code + * @param dfa determinized left tracer + * @param left nondeterm. left tracer (only needed for variables!) + * @param pat ? + * @param elementType ... + */ +abstract class RightTracerInScala extends Autom2Scala { + + val seqVars: Set; + val pat:Tree; + val elementType: Type; + + + def SeqTrace_headElem( arg: Tree ) = { // REMOVE SeqTrace + val t = Apply(Select(arg, definitions.List_head), Nil); + Apply(Select(t, definitions.tupleField(2,2)),Nil) + } + + def SeqTrace_headState( arg: Tree ) = { // REMOVE SeqTrace + val t = Apply(Select(arg, definitions.List_head), Nil); + Apply(Select(t, definitions.tupleField(2,1)), Nil) + } + + def SeqTrace_tail( arg: Tree ): Tree = // REMOVE SeqTrace + Apply(Select(arg, definitions.List_tail), Nil); + + final def collectVars(pat: Tree): HashSet = { + var vars = new HashSet(); + + def handleVariableSymbol(sym: Symbol): Unit = { + vars.add( sym ); + } + def isVariableName(name: Name): Boolean = + ( name != nme.WILDCARD ) && ( treeInfo.isVariableName( name ) ) ; + + def isVariableSymbol(sym: Symbol): Boolean = { + ( sym != null )&&( !sym.isPrimaryConstructor ); + } + + def traverse(tree: Tree): Unit = { + tree match { + case x @ Ident(name)=> + if(x.symbol != definitions.PatternWildcard) + scala.Predef.error("shouldn't happen?!"); + + case Star(t) => + traverse(t); + case Bind(name, subtree) => + var sym: Symbol = null; + if( isVariableName( name ) + && isVariableSymbol( {sym = tree.symbol; tree.symbol} )) + handleVariableSymbol( sym ); + + traverse( subtree ); + + // congruence cases + case Apply(fun, args) => args foreach {traverse}; + case Sequence(trees) => trees foreach {traverse}; + case Typed(expr, tpe) => traverse(expr); // needed?? + case _ : Alternative | _ : Select | _ : Literal => ; // no variables + case _ => Predef.error("unknown node:"+tree+" = "+tree.getClass()); + } + } + traverse( pat ); + return vars; + } + + //final def defs = cf.defs; + + val allVars: Set = collectVars( pat ); + + var varsToExport: Set = new HashSet(); // @todo HANDLE seqVars THESE GLOBALLY INSTEAD OF LOCALLY + + varsToExport.addAll( allVars ); + varsToExport.removeAll( seqVars ); + + var targetSym:Symbol = _; + + var helpMap = new HashMap(); + var helpMap2 = new HashMap(); + var helpVarDefs:scala.List[Tree] = Nil; + + val it = seqVars.iterator(); + while(it.hasNext()) { + makeHelpVar( it.next().asInstanceOf[Symbol] ); + } + + val jt = allVars.iterator(); + while(jt.hasNext()) { + val varSym = jt.next().asInstanceOf[Symbol]; + if(( varSym.name.toString().indexOf("$") == -1 ) + && ( !seqVars.contains( varSym ))) { + makeHelpVar( varSym, true ); + } + } + + //System.out.println("allVars: "+allVars); + //System.out.println("seqVars: "+seqVars); + //System.out.println("helpVarDefs now: "+helpVarDefs); + + initializeSyms(); + + def makeHelpVar(realVar: Symbol): Unit = { + makeHelpVar( realVar, false ); + } + + /** makes a helpvar and puts mapping into helpMap, ValDef into helpVarDefs + */ + + def makeHelpVar(realVar: Symbol, keepType: Boolean): Unit = { + val helpVar = owner.newVariable( pos, + fresh.newName( realVar.name + .toString()+"RTIS" )); + var rhs: Tree = null; + + //System.out.println("RTiS making helpvar : "+realVar+" -> "+helpVar); + + if( keepType ) { + helpVar.setInfo( realVar.tpe ); + rhs = EmptyTree; + } else { + helpVar.setInfo( definitions.ListClass.info /* LIST_TYPE(elementType)*/ ); + rhs = gen.mkNil; + } + + helpMap.put( realVar, helpVar ); + helpVar.setFlag(Flags.MUTABLE); + val varDef = ValDef( helpVar, rhs ); + //((ValDef) varDef).kind = Kinds.VAR; + helpVarDefs= varDef :: helpVarDefs; + + } + + def prependToHelpVar(realVar: Symbol, elem:Tree): Tree = { + val hv = refHelpVar( realVar ); + Assign( hv, gen.mkNewCons( /*elementType, */elem, hv )); + /* + return cf.Block(pos, + new Tree [] { + cf.debugPrintRuntime( "ASSIGN" ), + gen.Assign( hv, cf.newSeqCons( elem, hv )) + }, defs.UNIT_TYPE()); + */ + } + + protected def initializeSyms(): Unit = { + + this.funSym = owner.newLabel( pos, fresh.newName( "right" )); + + this.iterSym = owner.newVariable( pos, fresh.newName("iter")) + .setInfo( SeqTraceType( elementType )); + + this.stateSym = owner.newVariable ( pos, fresh.newName("q")) + .setInfo( definitions.IntClass.info ) ; + + this.curSym = owner.newVariable( pos, fresh.newName("cur")) + .setInfo( elementType ) ; + + this.targetSym = owner.newVariable( pos, fresh.newName("p")) + .setInfo( definitions.IntClass.info ) ; + + funSym.setInfo( + MethodType( scala.List ( // dummy symbol MethodType + SeqTraceType(elementType), + //funSym.newValueParameter( pos, fresh.newName("iter") /*, SeqTraceType elementType */), + definitions.IntClass.info), + //funSym.newValueParameter( pos, fresh.newName( "q" ) /*, definitions.IntClass.info */), + definitions.UnitClass.info)) // result + + } + + // load current elem and trace + override def loadCurrentElem(body: Tree): Tree = { + If( isEmpty( _iter() ), + run_finished( 0 ), // we are done + Block( scala.List ( + ValDef( this.targetSym, + SeqTrace_headState( Ident( iterSym))), + ValDef( this.curSym, + SeqTrace_headElem( Ident( iterSym )))), + body ) + ); + } + + /** see code_state0_NEW + */ + def code_state0(elseBody: Tree) = { // careful, map Int to Int + + If( Equals( _state(), Literal(0)), + code_state0_NEW(), + elseBody ); + + } + + /** this one is special, we check the first element of the trace + * and choose the next state depending only on the state part + */ + def code_state0_NEW(): Tree = { // careful, map Int to Int + + val hmap = dfa.deltaq( 0 ); // all the initial states + + var i = 0; + val n = hmap.keySet().size(); // all transitions defined + + val tmapTag = new TreeMap(); + val tmapBody = new TreeMap(); + var it = hmap.keySet().iterator(); + while(it.hasNext()) { + val targetL = it.next().asInstanceOf[Integer]; + val targetR = hmap.get( targetL ).asInstanceOf[Integer]; + + val I = new Integer( i ); + tmapTag.put( targetL, I ); + tmapBody.put( I, callFun( scala.List ( + SeqTrace_tail( _iter() ), + Literal( targetR.intValue() ) ))); + i = i + 1; + } + //i = 0; + + var ncases: scala.List[CaseDef] = Nil; + //val tags = new Array[Int]( n ); + //val targets = new Array[Tree]( n ); + var jt = tmapTag.keySet().iterator(); + while(jt.hasNext()) { + val tagI = jt.next().asInstanceOf[Integer]; + //tags( i ) = tagI.intValue(); + val I = tmapTag.get( tagI ).asInstanceOf[Integer]; + //targets( i ) = tmapBody.get( I ).asInstanceOf[Tree];; + ncases = CaseDef( Literal(tagI.intValue()), + tmapBody.get(I).asInstanceOf[Tree] ) :: ncases; + //i = i + 1 + } + //gen.Switch( gen.Ident( pos, targetSym ), + // tags, + // targets, + // code_error()/*cannot happen*/ ); + + Match(Ident(targetSym), ncases); + } + + override def currentMatches(label: Label): Tree = label match { + case LPair( target, theLab ) => + Equals( Literal(target.intValue() ), current() ); + case _ => + scala.Predef.error("expected Pair label"); + } + + + override def code_state_NEW(i: Int): Tree = { // precondition i != 0 + var stateBody = code_delta( i, DefaultLabel() ); + if( stateBody == null ) { + stateBody = code_error(); + } + val trans = dfa.deltaq( i ); + val tmapTag = new TreeMap(); + val tmapBody = new TreeMap(); + var j = 0; + var labs = dfa.labels().iterator(); + while(labs.hasNext()) { + val label = labs.next(); + val next = trans.get( label ).asInstanceOf[Integer]; + val action = code_delta( i, label.asInstanceOf[Label] ); + + if( action != null ) { + val J = new Integer( j ); + tmapTag.put( label.asInstanceOf[LPair].state, J ); + tmapBody.put( J, action ); + + stateBody = If( currentMatches( label.asInstanceOf[Label] ), + action, + stateBody); + } + j = j + 1; + } + val n = tmapTag.keySet().size(); + //j = 0; + //val tags = new Array[int]( n ); + //val targets = new Array[Tree]( n ); + var ncases: scala.List[CaseDef] = Nil; + val it = tmapTag.keySet().iterator(); + while(it.hasNext()) { + val tagI = it.next().asInstanceOf[Integer]; + //tags( j ) = tagI.intValue(); + val J = tmapTag.get( tagI ).asInstanceOf[Integer]; + //targets( j ) = tmapBody.get( J ).asInstanceOf[Tree]; + ncases = CaseDef(Literal(tagI.intValue()), + tmapBody.get( J ).asInstanceOf[Tree]) :: ncases; + //j = j + 1; + } + if( n > 0 ) + //gen.Switch( gen.Ident( pos, targetSym ), tags, targets, code_error() ); + Match(Ident( targetSym ), ncases); + else + code_error(); + } + + // calling the AlgebraicMatcher here + override def _cur_match(pat1: Tree): Tree = { + var pat = pat1; + //System.out.println("RTiS._cur_match("+pat.toString()+")"); + //System.out.println("calling algebraic matcher on type:"+pat.type); + + //System.err.println( "curT"+currentElem().type().widen() ); + val m = new PartialMatcher { + val owner = RightTracerInScala.this.owner; // , //funSym,//this.funSym, + val selector = currentElem(); //, + // result type defs.boolean_TYPE() ); + } + val freshenMap = new HashMap(); // sym2exp -> new sym + val helpMap3 = new HashMap(); // new sym -> original sym + + // "freshening": never use the same symbol more than once + // (in later invocations of _cur_match) + + var it = varsToExport.iterator(); + while(it.hasNext() ) { + val key = it.next().asInstanceOf[Symbol]; + if( key.name.toString().indexOf("$") == -1 ) { + this.helpMap2.put( key, helpMap.get( key )); + // "freshening" by appending string ( a bit dangerous ) + val newSym = key.cloneSymbol( owner /*funSym*/ ); + newSym.name = newTermName(key.name.toString() + "%") ; // erm + freshenMap.put( key, newSym ); + helpMap3.put( newSym, helpMap.get( key )); + //System.out.println( "key: "+ key + " key.owner:"+key.owner()); + //System.out.println( "newsym owner:"+newSym.owner()); + } else { + freshenMap.put( key, key ); + } + } + + //System.out.println("RightTracerInScala:: -pat :"+pat.toString()); + /* + System.out.println("RightTracerInScala - the seqVars"+seqVars); + System.out.println("RightTracerInScala - the varsToExport"+varsToExport); + */ + //System.out.println("RightTracerInScala::freshenMap :"+freshenMap); + + // "freshening" + + //@nsc @todo @todo @todo @todo + + //val tc = new TreeCloner( global, freshenMap, Type.IdMap ); + //pat = tc.transform( pat ); + //@nsc this commented out, is broken anyway. + + // val match case <pat> => <do binding>; true + // case _ => false + + + var ts: scala.List[Tree] = scala.List(); //new Array[Tree]( helpMap3.keySet().size() ); + //var j = 0; + var jt = helpMap3.keySet().iterator(); + while(jt.hasNext()) { + val vsym = jt.next().asInstanceOf[Symbol]; + val hv = helpMap3.get( vsym ).asInstanceOf[Symbol]; + //hv.setInfo( defs.LIST_TYPE( elementType ) ) ; DEBUG ALARM ? + ts = Assign( Ident(hv), Ident(vsym) ) :: ts; + //ts( j ) = gen.; + //j = j + 1; + // System.out.println( "the assign" + res[ j - 1 ] ); + } + + val theBody = Block(ts, Literal( true )); // just `true' + + am.construct( m, scala.List( + CaseDef( pat, theBody), // freshening + // if tree val matches pat -> update vars, return true + CaseDef(Ident(definitions.PatternWildcard), Literal(false))), + // else return false + true // do binding please + ); + + am.toTree(); + } + + /** returns translation of transition with label from i. + * returns null if there is no such transition(no translation needed) + */ + override def code_delta(i: Int , label: Label ): Tree = { + val hmap = dfa.deltaq( i ); + val ntarget = hmap.get( label ).asInstanceOf[Integer]; + var algMatchTree: Tree = null; + if( ntarget == null ) + return null; + + //System.out.println("delta("+i+","+label+")" ); + var theLab: Label = null; + label match { + case LPair ( state, lab2 )=> + //assert ntarget == state; + theLab = lab2; + lab2 match { + case TreeLabel( pat ) => + algMatchTree = _cur_match( pat ); + case _ => + } + case DefaultLabel() => + scala.Predef.error("bla"); // should not happen + } + //assert dfa.qbinders != null : "qbinders ?"; + + var vars = dfa.qbinders(i); + + //System.out.println("dfa.qbinders[ i ]"+vars); + + if (null == vars) vars = new Vector(); // TODO: make this more consistent + //assert vars != null; + + var stms = if (algMatchTree != null ) algMatchTree::Nil else Nil; + + var it = vars.iterator(); + while(it.hasNext()) { + val vble = it.next().asInstanceOf[Symbol]; + val rhs = gen.Ident( curSym ); + stms = prependToHelpVar( vble , rhs) :: stms; + } + + val value = callFun( scala.List( SeqTrace_tail( _iter() ), + Literal(ntarget.intValue()))); + + Block(stms, value ); + } + + override def stateWrap(i: Int): Tree = { + if (i == 0) + code_state0_NEW(); + else + code_state_NEW(i); + } + + /* returns statements that do the work of the right-transducer + */ + def getStms(trace: Tree, unit: CompilationUnit, body: Tree): Tree = { + + var stms: scala.List[Tree] = scala.List(); + val loopbody = code_body_NEW(); + + stms = ( + scala.List( + ValDef( iterSym, trace ), + ValDef( stateSym, Literal( 0 )) + ) ::: helpVarDefs + ::: scala.List( + LabelDef( + this.funSym, + scala.List ( + iterSym, + stateSym + ), + loopbody ) + )); + + // bind variables handled by this righttracer + var it = seqVars.iterator(); + while(it.hasNext()) + stms = stms ::: bindVar( it.next().asInstanceOf[Symbol] ) :: Nil; + + val treeCloner = new Transformer { + override def transform(tree1: Tree): Tree = { + val tree = super.transform(tree1); + if (tree.hasSymbol) { + val symbol = helpMap2.get(tree.symbol); + if (symbol != null) tree.setSymbol(symbol.asInstanceOf[Symbol]); + } + tree; + } + }; + + Block(stms, treeCloner.transform( body )); + } + + + /** return the accumulator. (same as in LeftTracerInScala) + * todo: move tree generation of Unit somewhere else + */ + override def run_finished(state: Int): Tree = Literal(()); + + def current() = Ident( targetSym ); + + def refHelpVar(realVar: Symbol) = { + val hv = helpMap.get( realVar ).asInstanceOf[Symbol]; + //assert hv != null : realVar; + Ident(hv); + } + + def assignToHelpVar(realVar: Symbol, rhs: Tree): Tree = { + val hv = refHelpVar(realVar); + Assign(hv, rhs); + } + + def bindVar(realVar: Symbol): Tree = { + val hv = refHelpVar(realVar); + /* + System.out.println("binding realVar.name "+realVar.name+" type:"+realVar.type()+" to hv type:"+hv.type()); + realVar.setOwner( owner ); + System.out.println("is same as realVar"+realVar.type().isSameAs( elementType )); + System.out.println("is same as hv"+realVar.type().isSameAs( hv.type() )); + if( realVar.type().isSameAs( elementType )) + return gen.ValDef( realVar, SeqList_head( hv )); + else + return gen.ValDef( realVar, hv ); + */ + if( isSameType(realVar.tpe, hv.tpe)) + ValDef( realVar, hv ); // e.g. x @ _* + else { + ValDef( realVar, SeqList_head( hv )); + } + } +} +} diff --git a/src/compiler/scala/tools/nsc/matching/SequenceMatchers.scala b/src/compiler/scala/tools/nsc/matching/SequenceMatchers.scala new file mode 100644 index 0000000000..d2d8aa7414 --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/SequenceMatchers.scala @@ -0,0 +1,173 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ + +package scala.tools.nsc.matching; + +import java.util._ ; + +/** constructs a matcher for a sequence pattern. plays two roles in + * described in design pattern Builder. + * is the "Director" for "Builder" class BerrySethi. + * is the "Builder" for "Director" class TransMatch. + */ + +trait SequenceMatchers: TransMatcher { + + import global._; + + class SequenceMatcher { + + // Console.println("CONSTR SEQUENCEMATCHER"); + final val IGNORED = new Integer(42); + + var _m: PartialMatcher = _; + + var bbuild: BindingBerrySethi = null; + + /** collects variables + * @return all variables bound by this binding nfa + */ + def collectNfaVariables(nfa: NondetWordAutom): Set = { + val seqVars = new HashSet(); + var j = 0; + while(j < nfa.nstates) { + if( nfa.qbinders( j ) != null ) + seqVars.addAll( nfa.qbinders( j ) ); + j = j + 1 + } + seqVars; + } + + + /** translates the det/switching automaton to scala code + * precondition: pat.type() corresponds to element type + */ + def addBinderToBody( pat1:Tree , body:Tree ): Tree = { + if( bbuild == null ) + bbuild = new BindingBerrySethi; + + val elementType1 = getElemType_Sequence( pat1.tpe ); + + // (a) build *binding* nfa (sequential machine) + val left = bbuild.automatonFrom( pat1, IGNORED ); + val right = bbuild.revnfa; + + // (b) determinize + translate L + + val dLeft = new DetWordAutom( left ); + + val ltis = new LeftTracerInScala { + val dfa = dLeft; + val owner = _m.owner; + val selector = _m.selector; + val elementType = elementType1; + } + + val trace = ltis.getTrace(); + + // (c) determinize + translate R + + val dRight = new DetWordAutom( right, left, dLeft ); + + val seqVars1 = collectNfaVariables( left ); + //System.out.println("seqVars here are:"+seqVars); + val rtis = new RightTracerInScala { + val dfa = dRight; + val owner = _m.owner; + val pat = pat1; + val seqVars = seqVars1; + val elementType = elementType1; + } + + // !!! Tree stms2 = rtis.getStms( theTrace, cunit, body ); + // !!! gen.mkBlock_( body.pos, trace, stms2 ); + val stms2 = rtis.getStms( trace, cunit, body ); + stms2; + } + + private def buildNfas( pat:scala.List[Tree] ): Array[NondetWordAutom] = { + val build = new BerrySethi; + val manyNfa = new Array[NondetWordAutom]( pat.length ); + var i = 0; + val it = pat.elements; + while( i < pat.length ) { + manyNfa( i ) = build.automatonFrom( it.next, new Integer( i )); + i = i + 1; + //manyNfa[ i ].print(); + } + manyNfa; + } + + /** constructs a word recognizer from an array of patterns which + * should all be SequencePatterns ( no wildcard * ) + * precondition: pat.type corresponds to element type + * @param _m Matcher object, holds the result + * @param pat the (Sequence) patterns + * @param body the bodies + * @param defaultCase code that is run when nothing matches. may be null, it + * becomes a ThrowMatchError then + * @param doBinding flasg that indicates whether variables should be bound + */ + def construct(_m: PartialMatcher, pat: scala.List[Tree] , body: scala.List[Tree] , defcase1: Tree, doBinding: Boolean ): Unit = { + var defaultCase = defcase1; + this._m = _m; + //assert body.length == pat.length; + if( defaultCase == null ) + defaultCase = ThrowMatchError( _m.pos, resultType ); + + val seqType = pat( 0 ).tpe; + val elementType1 = getElemType_Sequence( seqType ); + + // STEP 1 - build nfas for each pattern + + val manyNfa = buildNfas( pat ); + + // STEP 2 - recognizing + + // (a) merge nfas into one if necessary + val nfa = if(pat.length > 1) + new NondetWordAutom( manyNfa ) + else + manyNfa( 0 ); + + //nfa.print(); + + // (b) determinize + val dfa1 = new DetWordAutom( nfa ); + + // (c) translate to scala code + val scalaAut = + new WordAutomInScala{ + val dfa = dfa1; + val owner = _m.owner; + val optim = settings.target == "jvm"; + val elementType = elementType1; + } + scalaAut.translate(); + + // STEP 3 - binding + + var newbody: scala.List[Tree] = Nil; + if( !doBinding ) + newbody = body; + else { // this is done in the body of the matching case + var i = 0; + while(i < body.length) { + if( !containsBinding( pat( i ) ) ) + newbody = body( i ) :: newbody; // no need for binding + else + newbody = addBinderToBody( pat( i ), body( i ) ) :: newbody; + i = i + 1; + } + newbody = newbody.reverse; + } + _m.tree = scalaAut.getMatcherSwitch( _m.selector, + defaultCase, + newbody ); + } // construct (PartialMatcher, Tree[], Tree[], Tree, boolean ) + + } // class SequenceMatcher +} diff --git a/src/compiler/scala/tools/nsc/matching/StateSetComparator.scala b/src/compiler/scala/tools/nsc/matching/StateSetComparator.scala new file mode 100644 index 0000000000..99942f38d5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/StateSetComparator.scala @@ -0,0 +1,34 @@ +package scala.tools.nsc.matching ; + +import java.util.{Comparator, TreeSet} ; + +class StateSetComparator extends Comparator { + // use lexicographic order + def compare(o1: Any , o2: Any ): Int = { + /* + System.out.print("lexi" ); + System.out.print( o1 +" "); + System.out.println( o2 ); + */ + val it1 = o1.asInstanceOf[TreeSet].iterator(); + val it2 = o2.asInstanceOf[TreeSet].iterator(); + while( it1.hasNext() ) { + while( it2.hasNext() ) { + if( !it1.hasNext() ) + return -1; + + val i1 = it1.next().asInstanceOf[Integer].intValue(); + val i2 = it2.next().asInstanceOf[Integer].intValue(); + if( i1 < i2 ) + return -1; + else if ( i1 > i2 ) + return 1; + } + if( it1.hasNext() ) + return 1; + } + if( it2.hasNext() ) + return -1; + return 0; + } +} diff --git a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala new file mode 100644 index 0000000000..bca478b0dc --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala @@ -0,0 +1,301 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ +package scala.tools.nsc.matching; + +/** Translation of pattern matching + */ +abstract class TransMatcher extends transform.Transform +with PatternNodes +with CodeFactory +with PatternMatchers +with SequenceMatchers +with AlgebraicMatchers +with MatcherLabels +with BerrySethis +with DetWordAutoms +with NondetWordAutoms +with Autom2 +with WordAutoms +with LeftTracers +with RightTracers { + + import global._; + import definitions._; + import posAssigner.atPos; + import typer.typed; + + val phaseName = "transmatcher"; + + protected def newTransformer(unit: global.CompilationUnit): global.Transformer = { + cunit = unit; + new TransMatch + } + + /** container. classes AlgebraicMatcher and SequenceMatcher get input and + * store their results in here. resembles the 'Memento' design pattern, + * could also be named 'Liaison' + */ + abstract class PartialMatcher { + + /** owner of the code we create (input) + */ + val owner: Symbol; + + /** the selector value (input) + */ + val selector:Tree; + + /** tree representing the matcher (output) + */ + var tree: Tree = _ ; + + def pos: int = selector.pos; + + //assert( owner != null ) : "owner is null"; + //assert owner != Symbol.NONE ; + //this.owner = owner; + + //assert root != null; + //assert root.type != null; + //this.selector = root; + + //assert this.resultType != Type.NoType; + //this.resultType = resultType; + + //this.pos = root.pos; // for convenience only + + } + + var cunit: CompilationUnit = _; + + def fresh = cunit.fresh ; + + var currentOwner: Symbol = _; + + var resultType: Type = _; + + def containsBinding(pat: Tree): Boolean = { + var generatedVars = false; + + def handleVariableSymbol(sym: Symbol): Unit = + if (sym.name.toString().indexOf("$") == -1) { + generatedVars = true; // .add(sym); + } + + def isVariableName(name: Name): Boolean = + ( treeInfo.isVariableName(name) ) && ( name != nme.USCOREkw ) ; + + def isVariableSymbol(sym: Symbol): Boolean = + ( sym != null )&&( !sym.isPrimaryConstructor ); + + def traverse(tree: Tree): Unit = { + + tree match { + case x @ Ident(name) => + if(x.symbol != definitions.PatternWildcard) + error("shouldn't happen?!"); + + case Bind(name, subtree) => + var sym: Symbol = null; + + if (isVariableName(name) + && isVariableSymbol( {sym = tree.symbol; tree.symbol} )) + handleVariableSymbol(sym); + + traverse( subtree ); + + // congruence + + case Apply(fun, args) => args foreach traverse; + case Sequence(trees) => trees foreach traverse + case Star(arg) => traverse(arg) + case Typed(expr, tpe) => traverse(expr); // needed?? + + case _ : Select | + _ : Alternative | + _ : Select | + _ : Literal => ; // no variables + + case _ => + error("unknown pattern node:" + tree + " = " + tree.getClass()); + } + } + traverse(pat); + generatedVars; + } + + class TransMatch extends Transformer { + + /** a casedef with sequence subpatterns like + * + * case ..x @ ().. => body + * + * should be replaced straight away with + * + * case .. () .. => val x = Nil; body + */ + def isRegular(pats:List[CaseDef]): Pair[List[CaseDef],Boolean] = { + var existsReg = false; + var isReg = false; + var nilVars:List[Symbol] = null; + + def isRegular1(pat: Tree): Tree = pat match { + case Alternative(trees) => + copy.Alternative(pat, trees map { isRegular1 }); + + case Star(t) => isReg = true; copy.Star(pat, isRegular1(t) ); + + case Ident(_) => pat; + + case Bind( id, empt @ Sequence(List())) => + nilVars = pat.symbol /*id.symbol()*/ :: nilVars; + empt; + case Bind( n, pat1 ) => copy.Bind(pat, n, isRegular1( pat1 )); + + case Sequence( trees ) => + //isReg = isReg || ( trees.length == 0 ); + isReg = true; // cause there are ArrayValues now + copy.Sequence(pat, trees map { isRegular1 }); + + case ArrayValue( tt, List(b @ Bind(id, Star(wc @ Ident(nme.WILDCARD))))) => + //Console.println("OPTIMIZING"); + //Console.println(pat); + //Console.println(pat.tpe); + //Console.println(b.tpe); + b.symbol.setInfo(pat.tpe); + b.setType(pat.tpe); + val res = copy.Bind(b, id, wc); + //Console.println("====>"); + //Console.println(res); + res + + case ArrayValue( s, trees ) => + //Console.println("XXX ArrayValue, trees="+trees); + copy.ArrayValue( pat, s, (trees map { isRegular1 })); + + case Apply( fn, List(Sequence(List()))) => + pat; + + case Apply( fn, trees ) => + //Console.println(" IN isRegular, apply node "+pat.toString()); + //Console.println(" trees are:"+(trees map {x => x.getClass().toString()})); + copy.Apply( pat, fn, ( trees map { isRegular1 }) ) + + case Literal(_) => pat + case Select(_,_) => pat + case Typed(_,_) => pat + + //case _ => + // Console.println(pat); + // Console.println(pat.getClass()); + // scala.Predef.error(" what is this ? ") + } + + var res:List[CaseDef] = Nil; + val it = pats.elements; while(it.hasNext) { + nilVars = Nil; + val cdef = it.next; + val newt = isRegular1(cdef.pat); + existsReg = existsReg || isReg; + + val nbody = if(nilVars.isEmpty) cdef.body else + atPos(cdef.body.pos)( + Block(nilVars map { + x => ValDef(x, Ident(definitions.NilModule)) + }, cdef.body) + ); + + res = copy.CaseDef(cdef, newt, cdef.guard, nbody) :: res; + } + Pair(res.reverse, existsReg); + } + + + + def handle(sel:Tree, ocases:List[CaseDef]): Tree = { + + + // 1. is there a regular pattern? + + val Pair(cases, containsReg) = isRegular(ocases); + + // @todo: remove unused variables + + if(containsReg) { + // 2. replace nilVariables + //@todo: bring over AlgebraicMatcher + /* + val matcher = new PartialMatcher { + val global: TransMatcher.this.global.type = TransMatcher.this.global; + val owner = currentOwner; + val selector = sel ; + } + new AlgebraicMatcher() { + val tm: TransMatcher.this.type = TransMatcher.this; + }.construct( matcher, cases ); + matcher.tree + */ + System.out.println("" + sel + " match " + ocases); + scala.Predef.error("regular expressions not yet implemented"); + } else { + val pm = new PatternMatcher(); + pm.initialize(sel, currentOwner, true ); + pm.construct( cases ); + //if (global.log()) { + // global.log("internal pattern matching structure"); + // pm.print(); + //} + pm.toTree() + } + } + override def transform(tree: Tree): Tree = tree match { + /* // test code to generate forward jumps + case Match(selector, CaseDef(Ident(nme.WILDCARD), EmptyTree, exp)::Nil) => // inserting test-code + + val target1 = currentOwner.newLabel(tree.pos, "target1") + .setInfo(new MethodType(List(definitions.IntClass.tpe), definitions.IntClass.tpe)); + + val a = currentOwner.newValue(tree.pos, "a").setInfo(definitions.IntClass.tpe); + val x = currentOwner.newValueParameter(tree.pos, "x").setInfo(definitions.IntClass.tpe); + val y = currentOwner.newValueParameter(tree.pos, "y").setInfo(definitions.IntClass.tpe); + val target2 = currentOwner.newLabel(tree.pos, "target2") + .setInfo(new MethodType(List(definitions.IntClass.tpe, definitions.IntClass.tpe), definitions.IntClass.tpe)); + + val z = currentOwner.newValueParameter(tree.pos, "z").setInfo(definitions.IntClass.tpe); + typed { atPos(tree.pos) { Block( + List( + Apply(Ident(target2), List(Literal(Constant(1)),Literal(Constant(2)))), + LabelDef(target1, List(x), exp)), + LabelDef(target2, List(y,z), Apply(Select(Ident(y), nme.PLUS), List(Ident(z)))) + )}}; +*/ + + case Match(selector, cases) => + val nselector = transform(selector).setType(selector.tpe); + val ncases = cases map { transform }; +/* + Console.println("TransMatch translating cases: "); + for(val t <- cases) { + Console.println(t.pat.toString()); + Console.println("BODY "+t.body.toString()); + } +*/ + // @todo: remove from partial matcher + TransMatcher.this.currentOwner = currentOwner; + TransMatcher.this.resultType = tree.tpe; + //Console.println("TransMatcher currentOwner ="+currentOwner+")"); + //Console.println("TransMatcher selector.tpe ="+selector.tpe+")"); + //Console.println("TransMatcher resultType ="+resultType+")"); + val t_untyped = handle(nselector, ncases.asInstanceOf[List[CaseDef]]); + //Console.println("t_untyped "+t_untyped.toString()); + val t = typed { atPos(tree.pos) (t_untyped) }; + //Console.println("t typed "+t.toString()); + t + case _ => + super.transform(tree); + } + } +} diff --git a/src/compiler/scala/tools/nsc/matching/WordAutoms.scala b/src/compiler/scala/tools/nsc/matching/WordAutoms.scala new file mode 100644 index 0000000000..de17fc6445 --- /dev/null +++ b/src/compiler/scala/tools/nsc/matching/WordAutoms.scala @@ -0,0 +1,146 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ + +package scala.tools.nsc.matching; + +import java.util._ ; + +import scala.tools.nsc.util.Position; + +trait WordAutoms: TransMatcher { + + import global._ ; + /** translates a recognizer to scala code + */ + + /** constructor + * @param dfa the dfa that is to be translated + * @param elementType type of the objects in the sequence + * @param owner the owner of the pattern match + * @param cf code factory + * @param optim flag that indicates whether to optimize + * @return an object that translates the dfa + */ +abstract class WordAutomInScala extends Autom2Scala { + + val optim: Boolean; + + this.optimize = this.optimize && optim; + + var theLabelDef: Tree = _ ; + + def getMatcherSwitch(selector: Tree, failTree: Tree, body: scala.List[Tree] /*, resultType: Type*/ ): Tree = { + + var result: Tree = null; + var ncases: scala.List[CaseDef] = Nil; + val it = body.reverse.elements; + //val tags = new Array[int](body.length); + var i = body.length - 1; + while( i >= 0 ) { + //tags(i) = i; + ncases = CaseDef(Literal(i), it.next) :: ncases; + i = i - 1 + } + + ncases= ncases::: CaseDef(Ident(nme.WILDCARD),failTree) :: Nil; + result = Match( _swres(), ncases ); + + //} + + result = + Block( + scala.List ( + ValDef( iterSym, newIterator( selector.duplicate )), + ValDef( stateSym, Literal(0) ), + ValDef( resultSym, theLabelDef )), + result + ); + //unit.global.debugPrinter.print( result ); + result; + } + + protected def initializeSyms(): Unit = { // TEST + + this.funSym = owner.newLabel( pos, fresh.newName( "matcher" )); + + this.iterSym = owner.newVariable( pos, fresh.newName("iter")) + .setInfo( _seqIterType( elementType ) ) ; + + this.stateSym = owner.newVariable( pos, fresh.newName("q")) + .setInfo( definitions.IntClass.info ) ; + + this.resultSym = owner.newVariable( pos, fresh.newName("swRes")) + .setInfo( definitions.IntClass.info ) ; + + this.funSym.setInfo( MethodType(scala.List (definitions.IntClass.info), + definitions.IntClass.info )); + + this.curSym = owner.newVariable( pos, "cur" /*Names.cur*/ ) + .setInfo( elementType ); + + this.hasnSym = owner.newVariable( pos, nme.hasNext ) + .setInfo( definitions.BooleanClass.info ); + + } + + /** code for the return value of the automaton translation + */ + override def run_finished(state: Int): Tree = { // T E S T + if( dfa.isFinal(state)) + Literal(dfa.finals.get(new Integer(state)).asInstanceOf[Integer].intValue()); + else + Literal(FAIL); + } + + + // calling the /*AlgebraicMatcher*/PatternMatcher here + override def _cur_match(pat: Tree): Tree = { // TE ST + val m = new PartialMatcher { + val owner = WordAutomInScala.this.owner; /* owner*/ + val selector = currentElem(); /* root */ + // restyp definitions.BooleanClass.info /* restype */); + } + + am.construct( m, scala.List ( + CaseDef( pat, Literal( true )), + CaseDef( Ident(definitions.PatternWildcard), Literal( false )) ), + false); + am.toTree(); + } + + /** do the translation + */ + def translate(): Unit = { + initializeSyms(); + val tb = code_body_NEW(); + //theLabelDef = gen.DefDef(this.funSym, tb); + theLabelDef = LabelDef(this.funSym, scala.List( stateSym ), tb); + } + + /** ... + * @return returns translation of transition with label from i. + * returns null if there is no such transition + * (no translation needed) + */ + override def code_delta(i: Int, label: Label): Tree = { + val target = dfa.delta(i, label); + + if (target == null) + label match { + case DefaultLabel() => + code_error(); // this may not happen ! + case _ => + null; // not good + } + else if (target.intValue() == dfa.nstates() - 1) // that one is a dead state + code_fail(); + else + callFun(scala.List( Literal(target.intValue() ))); + } + +} +} + diff --git a/src/compiler/scala/tools/nsc/models/Models.scala.xxx b/src/compiler/scala/tools/nsc/models/Models.scala.xxx new file mode 100644 index 0000000000..e95976ed19 --- /dev/null +++ b/src/compiler/scala/tools/nsc/models/Models.scala.xxx @@ -0,0 +1,189 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id: Trees.scala,v 1.35 2005/11/14 16:58:21 mcdirmid Exp $ +package scala.tools.nsc.models; + +import scala.tools.nsc.Global; +import scala.tools.nsc.ast.Trees; +import scala.tools.nsc.symtab.Flags; +import scala.tools.nsc.symtab.Names; + +[_trait_] abstract class Models { + val global : Global; + + import global._; + + abstract class Model { + } + abstract class HasTree extends Model { + var tree : Tree = _; + def update(tree0 : Tree): Boolean = { + tree = tree0; + false; + } + def replacedBy(tree0 : Tree) : Boolean = true; + } + class ImportMod extends HasTree { + def treex = tree.asInstanceOf[Import]; + + override def replacedBy(tree0 : Tree) : Boolean = if (super.replacedBy(tree0) && tree0.isInstanceOf[Import]) { + val tree1 = tree0.asInstanceOf[Import]; + tree1.tpe == treex.tpe; + } else false; + } + + abstract class Composite extends Model { + import scala.collection.mutable._; + class Members extends HashSet[HasTree] with ObservableSet[HasTree, Members] { + override def +=(elem: HasTree): Unit = super.+=(elem); + override def -=(elem: HasTree): Unit = super.-=(elem); + override def clear: Unit = super.clear; + } + object members extends Members; + + def isMember(tree : Tree) : Boolean = false; + + def update0(members1 : List[Tree]) : Boolean = { + val marked = new HashSet[HasTree]; + var updated = false; + for (val mmbr1 <- members1) { + var found = false; + for (val mmbr <- members) if (!found && mmbr.replacedBy(mmbr1)) { + found = true; + updated = mmbr.update(mmbr1) || updated; + marked += mmbr; + } + if (!found) { + updated = true; + val add = modelFor(mmbr1); + add.update(mmbr1); + members += (add); + marked += add; + } + } + val sz = members.size; + members.intersect(marked); + updated = updated || sz < members.size; + // check if anything was removed! + updated; + } + } + abstract class MemberMod extends HasTree { + def treex = tree.asInstanceOf[MemberDef]; + def flags = new Flags.Flag(treex.mods0); + def name : Name = { treex.name0; } + + override def replacedBy(tree0 : Tree) : Boolean = if (super.replacedBy(tree0) && tree0.isInstanceOf[MemberDef]) { + val tree1 = tree0.asInstanceOf[MemberDef]; + treex.name0 == tree1.name0; + } else false; + + override def update(tree0 : Tree): Boolean = { + val updated = tree == null || (treex.mods0 != tree0.asInstanceOf[MemberDef].mods0); + super.update(tree0) || updated; + } + } + abstract class MemberComposite extends MemberMod with Composite; + + abstract class HasClassObjects extends Composite { + override def isMember(tree : Tree) : Boolean = super.isMember(tree) || tree.isInstanceOf[ImplDef]; + } + abstract class ValOrDefMod extends MemberComposite with HasClassObjects { + def treey = tree.asInstanceOf[ValOrDefDef]; + override def replacedBy(tree0 : Tree) : Boolean = (super.replacedBy(tree0) && tree0.isInstanceOf[ValOrDefDef]); + + override def update(tree0 : Tree): Boolean = { + val tree1 = tree0.asInstanceOf[ValOrDefDef]; + val updated = tree == null || treex.tpe != tree1.tpe; + update0(flatten(tree1.rhs0, (tree2 : Tree) => isMember(tree2))); + super.update(tree0) || updated; + } + } + class ValMod extends ValOrDefMod { + def treez = tree.asInstanceOf[ValDef]; + override def replacedBy(tree0 : Tree) : Boolean = (super.replacedBy(tree0) && tree0.isInstanceOf[ValDef]); + } + class DefMod extends ValOrDefMod { + def treez = tree.asInstanceOf[DefDef]; + + override def replacedBy(tree0 : Tree) : Boolean = if (super.replacedBy(tree0) && tree0.isInstanceOf[DefDef]) { + val tree1 = tree0.asInstanceOf[DefDef]; + if (tree1.vparamss.length == treez.vparamss.length) { + val tpz = for (val vd <- treez.vparamss) yield for (val xd <- vd) yield xd.tpe; + val tp1 = for (val vd <- tree1.vparamss) yield for (val xd <- vd) yield xd.tpe; + tpz == tp1; + } else false; + } else false; + } + abstract class ImplMod extends MemberComposite with HasClassObjects { + def treey = tree.asInstanceOf[ImplDef]; + override def replacedBy(tree0 : Tree) : Boolean = (super.replacedBy(tree0) && tree0.isInstanceOf[ImplDef]); + override def isMember(tree : Tree) : Boolean = super.isMember(tree) || tree.isInstanceOf[ValOrDefDef] || tree.isInstanceOf[AliasTypeDef]; + + override def update(tree0 : Tree): Boolean = { + val tree1 = tree0.asInstanceOf[ImplDef]; + + var updated = update0(tree1.impl0.body); + + // XXX: ignore parents for now! + + (super.update(tree0) || updated) && !flags.isPrivate; + } + + + } + class ClassMod extends ImplMod { + def treez = tree.asInstanceOf[ClassDef]; + override def replacedBy(tree0 : Tree) : Boolean = (super.replacedBy(tree0) && tree0.isInstanceOf[ClassDef]); + + override def update(tree0 : Tree): Boolean = { + // XXX: type parameters and this type! + + + super.update(tree0); + } + } + class ObjectMod extends ImplMod { + def treez = tree.asInstanceOf[ModuleDef]; + override def replacedBy(tree0 : Tree) : Boolean = (super.replacedBy(tree0) && tree0.isInstanceOf[ModuleDef]); + } + class AliasTypeMod extends MemberMod { + def treey = tree.asInstanceOf[AliasTypeDef]; + override def replacedBy(tree0 : Tree) : Boolean = (super.replacedBy(tree0) && tree0.isInstanceOf[AliasTypeDef]); + } + class SourceMod(val unit : CompilationUnit) extends Composite with HasClassObjects { + override def isMember(tree : Tree) : Boolean = super.isMember(tree) || tree.isInstanceOf[Import]; + } + + + def flatten0(exprs : List[Tree], filter: (Tree) => Boolean) : List[Tree] = + for (val expr <- exprs; val t : Tree <- flatten(expr,filter)) yield t; + + def flatten(expr : Tree, filter: (Tree) => Boolean) : List[Tree] = + if (filter(expr)) expr :: Nil; else expr match { + case Block(stats,last) => flatten0(stats, filter) ::: flatten(last, filter); + case If(cond,thenp,elsep) => flatten(cond,filter) ::: flatten(thenp,filter) ::: flatten(elsep,filter); + case Assign(lhs,rhs) => flatten(rhs,filter); + case CaseDef(pat,guard,body) => flatten(body,filter); + case Return(expr0) => flatten(expr0,filter); + case Throw(expr0) => flatten(expr0,filter); + case Try(block,catches,finalizer) => flatten(block,filter) ::: flatten(finalizer,filter) ::: flatten0(catches, filter); + case Match(selector,cases) => flatten(selector,filter) ::: flatten0(cases, filter); + case Apply(fun,args) => flatten(fun,filter) ::: flatten0(args,filter); + case TypeApply(fun,args) => flatten(fun,filter) ::: flatten0(args,filter); + case _ => Nil; + } + + + def modelFor(tree: Tree): HasTree = tree match { + case _: ValDef => new ValMod(); + case _: DefDef => new DefMod(); + case _: ClassDef => new ClassMod(); + case _: ModuleDef => new ObjectMod(); + case _: AliasTypeDef => new AliasTypeMod(); + case _: Import => new ImportMod(); + } + +} diff --git a/src/compiler/scala/tools/nsc/models/SemanticTokens.scala b/src/compiler/scala/tools/nsc/models/SemanticTokens.scala new file mode 100644 index 0000000000..51d127b54b --- /dev/null +++ b/src/compiler/scala/tools/nsc/models/SemanticTokens.scala @@ -0,0 +1,513 @@ + +package scala.tools.nsc.models; + +import scala.tools.nsc.{Global => Compiler}; +import scala.tools.nsc.symtab.{Flags,Names}; +import scala.tools.nsc.util.{NameTransformer,Position,SourceFile}; +import scala.collection.mutable.{HashMap,HashSet}; + + +class SemanticTokens(val compiler: Compiler) { + import compiler._; + + abstract class Kind {} + object OBJECT extends Kind; + object CLASS extends Kind; + object DEF extends Kind; + object VAL extends Kind; + object VAR extends Kind; + object ARG extends Kind; + object TPARAM extends Kind; + + // static constants here. + + abstract class Token { + def length : Int; + + def prev : HasNext; + def next : HasPrev; + } + + [_trait_] abstract class HasNext extends Token { + var next0 : HasPrev = _; + def next = next0; + } + [_trait_] abstract class HasPrev extends Token { + var prev0 : HasNext = _; + def prev = prev0; + } + abstract class Actual extends HasNext with HasPrev { + def convertToGap : Pair[Int,Actual] = { + val nextGap = next.isInstanceOf[Gap]; + val prevGap = prev.isInstanceOf[Gap]; + if (prevGap) { + val ret = prev.length; + val gap = prev.asInstanceOf[Gap]; + gap.setLength(gap.length + length); + if (nextGap) { + gap.setLength(gap.length + next.length); + gap.next0 = next.next; + next.next.prev0 = gap; + } else { + gap.next0 = next; + next.prev0 = gap; + } + new Pair(ret,gap); + } else if (nextGap) { + val gap = next.asInstanceOf[Gap]; + gap.setLength(gap.length + length); + gap.prev0 = prev; + prev.next0 = gap; + new Pair(0,gap); + } else { + prev.next0 = next; + next.prev0 = prev; + val gap = new Gap(prev); + gap.setLength(length); + new Pair(0,gap); + } + } + def insert(prev1 : HasNext) = { + next0 = prev1.next; + prev0 = prev1; + prev0.next0 = this; + next0.prev0 = this; + } + } + final class Gap extends Actual { + def this(prev1 : HasNext) = { + this(); + insert(prev1); + } + override def toString() = "gap-" + length; + + var length0 : Int = -1; + def length : Int = length0; + def setLength(length1 : Int) = length0 = length1; + + // already gap + override def convertToGap : Pair[Int,Actual] = new Pair(0, this); + } + class Process(val unit : CompilationUnit) { + def source = unit.source; + + val symbols = new HashMap[Symbol,Info]; + + + class Info(val symbol : Symbol) { + var defined : Def = _; + val uses = new HashSet[Use]; + symbols.update(symbol, this); + } + def info(symbol : Symbol) : Info = if (symbols.contains(symbol)) symbols(symbol) else new Info(symbol); + + abstract class Semantic(val symbol : Symbol) extends Actual { + val name = NameTransformer.decode(symbol.name.toString()).toString().trim(); + assert(symbol != NoSymbol); + + + def length = name.length(); + def info : Info = if (symbols.contains(symbol)) symbols(symbol) else new Info(symbol); + + def kind = { + val term0 = symbol; + if (false) null; + else if (term0.isVariable) VAR; + else if (term0.isValueParameter) ARG; + else if (term0.isMethod) DEF; + else if (term0.isClass) CLASS; + else if (term0.isModule) OBJECT; + else if (term0.isValue ) VAL; + else if (term0.isTypeParameter) TPARAM; + else { + // System.err.println("UNRECOGNIZED SYMBOL: " + term0 + " " + name); + null; + } + }; + } + class Def(symbol0 : Symbol) extends Semantic(symbol0) { + info.defined = this; + override def toString() = "def-" + name + "-" + symbol.getClass(); + } + class Use(symbol0 : Symbol) extends Semantic(symbol0) { + info.uses += this; + + override def toString() = "use-" + name; + } + val list = new TokenList; + + build(unit.body); + + // ok start building.... + def build[T <: Tree](trees : List[T]) : Unit = for (val tree : T <- trees) build(tree); + + def build(tree0 : Tree) : Unit = { + tree0 match { + //case tree: SymTree => System.err.println("TREE: " + tree.getClass() + " " + tree.symbol + " " + (new Position(unit.source, tree.pos)).dbgString); + case _ => + } + if (tree0.pos != Position.NOPOS) tree0 match { + + + case tree : ImplDef => + val pos = tree.namePos(unit.source); + if (pos == Position.NOPOS) { + // inner types. + // System.err.println("NOPOS: " + tree.getClass() + " " + (new Position(unit.source, tree.pos)).dbgString); + //Thread.dumpStack(); + } else buildDef(tree.symbol, tree.namePos(unit.source)); + tree match { + case cdef : ClassDef => build(cdef.tparams); + case _ => ; + } + build(tree.impl.parents); + build(tree.impl.body); + case tree : ValOrDefDef => + if (tree.symbol.hasFlag(Flags.ACCESSOR)) { + // ignore + ; + } else { + { + val pos = if (tree.name.toString().equals("<init>")) Position.NOPOS else tree.namePos(unit.source); + if (pos != Position.NOPOS) { + if (!tree.hasFlag(Flags.SYNTHETIC)) + buildDef(tree.symbol, pos); + } + } + + if (tree.isInstanceOf[DefDef]) { + val ddef = tree.asInstanceOf[DefDef]; + build(ddef.tparams); + + for (val l0 <- ddef.vparamss; val arg <- l0) { + + val pos0 = if (!unit.source.beginsWith(arg.pos, "val ")) arg.pos; + else unit.source.skipWhitespace(arg.pos + ("val ").length()); + buildDef(arg.symbol, pos0); + build(arg.tpt); + } + } + try { + build(tree.tpt); + } catch { + case e: Error => + System.err.println("VALDEF: " + tree + " " + tree.tpt + " " + tree.pos + " " + tree.tpt.pos); + throw e; + } + build(tree.rhs); + } + case tree : PackageDef => + //System.err.println("PACKAGE: " + tree.name); + if (false) { + val pos = tree.namePos(unit.source); + if (pos != Position.NOPOS) + buildDef(tree.symbol, pos); + } + build(tree.stats); + case tree : Function => + for (val arg <- tree.vparams) if (arg.pos != Position.NOPOS) { + val name = arg.name.toString().trim(); + val pos : Int = + if (unit.source.beginsWith(arg.pos, "val ")) unit.source.skipWhitespace(arg.pos + ("val ").length()); + else if (unit.source.content(arg.pos) == ':') { + var posx = arg.pos; + while (Character.isWhitespace(unit.source.content(posx - 1))) posx = posx - 1; + posx - name.length(); + } else arg.pos; + buildDef(arg.symbol, pos); + build(arg.tpt); + } + build(tree.body); + case tree : TypeTree => + val tree1 = if (tree.original != null) tree.original; else tree; + buildT(tree1, tree.tpe); + def buildT( tree : Tree, tpe : Type) : Unit = if (tree.pos != Position.NOPOS) tpe match { + case tpe0 : TypeRef => tree match { + case apt : AppliedTypeTree => + buildUse(tpe.symbol, apt.tpt.pos); + //System.err.println("APT: " + apt.tpt + " sym0=" + apt.tpt.symbol + " sym1=" + tpe0.sym + " " + " " + apt.args + " " + tpe0.args); + + buildTs (apt.args, tpe0.args); + case ident : Ident => buildUse(tpe0.sym, ident.pos); + case select : Select => + // System.err.println("BUILD_SELECT: " + select + " @ " + tpe0); + try { + build(select); + } catch { + case e : Error => + System.err.println("BUILD_SELECT: " + select + " @ " + tpe0 + " " + unit.source.dbg(select.pos)); + throw e; + } + case tpt : TypeTree => + //System.err.println("UNKNOWN TPT0: " + tpe0 + " " + tpe0.args + " " + tpt); + case sft : SelectFromTypeTree => + build(sft.qualifier); // XXX: broken + if (false) System.err.println("SFTT: " + sft + " sym=" + sft.symbol + " selector=" + sft.selector + " qual=" + sft.qualifier + " qual.sym=" + + sft.qualifier.symbol + + " qual.pos=" + unit.source.dbg(sft.qualifier.pos) + " symbol=" + sft.symbol + " type=" + tpe0 + + " type.sym=" + tpe0.symbol); + case _ => System.err.println("UNKNOWN TPT2: " + tree + " vs. " + tpe0 + " " + tree.getClass() + " " + unit.source.content(tree.pos)); + } + case tpe0 : MethodType => tree match { + case tpt: TypeTree => + if (tpt.original != null) buildT(tpt.original, tpe); + else { + System.err.println("UNKNOWN TPT3: " + tree + " vs. " + tpe0 + " " + unit.source.content(tree.pos)); + } + case ident : Ident => buildT(ident, tpe0.resultType); + case select : Select => buildT(select, tpe0.resultType); + case _ => System.err.println("UNKNOWN TPE: " + tree + " vs. " + tpe0 + " " + tree.getClass()); + } + case _ => // System.err.println("UNKNOWN: " + tree + " " + (if (tree != null) tree.getClass() else null) + " vs. " + tpe + " " + (if (tpe != null) tpe.getClass() else null) + " " + (if (tree != null) tree.pos else null)); + }; + def buildTs(trees : List[Tree], types : List[Type]): Unit = if (!trees.isEmpty || !types.isEmpty) { + buildT (trees.head, types.head); + buildTs(trees.tail, types.tail); + }; + case tree : AbsTypeDef => buildDef(tree.symbol, tree.namePos); + case tree : Bind => buildDef(tree.symbol, tree.pos); + build(tree.body); + case tree : Ident => buildUse(tree.symbol, tree.pos); + case tree : Select => + // System.err.println("SELECT: " + tree.qualifier + " ** " + tree.symbol + " " + selectPos(tree)); + try { + build(tree.qualifier); + } catch { + case e : Error => System.err.println("SELECTQ: " + tree + " " + tree.qualifier + " " + unit.source.dbg(tree.qualifier.pos)); throw e; + } + try { + if (tree.pos >= unit.source.content.length) + System.err.println("BAD_SELECT_QUALIFIER " + tree + " @ " + tree.pos); + else buildUse(tree.symbol, selectPos(tree)); + } catch { + case e : Error => System.err.println("SELECTU: " + tree + " " + tree.symbol + " " + unit.source.dbg(tree.pos)); throw e; + } + case tree : GenericApply => + //System.err.println("APPLY-FUN: " + tree.fun0 + " " + tree.args0); + build(tree.fun0); + //System.err.println("APPLY-ARG: " + tree.fun0 + " " + tree.args0); + build(tree.args0); + case tree : Typed => build(tree.expr); build(tree.tpt); + case tree : Import => + //System.err.println("IMPORT: " + tree.expr + " " + tree.selectors); + + build(tree.expr); + case tree : Block => build(tree.stats); build(tree.expr); + case tree : CaseDef => + build(tree.pat); build(tree.guard); build(tree.body); + case tree : Sequence => build(tree.trees); + case tree : Assign => build(tree.lhs); build(tree.rhs); + case tree : If => build(tree.cond); build(tree.thenp); build(tree.elsep); + case tree : New => build(tree.tpt); + case tree : Match => build(tree.selector); build(tree.cases); + case tree : Return => build(tree.expr); + case tree : LabelDef => build(tree.rhs); + case tree : Throw => build(tree.expr); + case tree : Try => build(tree.block); build(tree.catches); build(tree.finalizer); + case tree : Literal => ; + case tree : This => ; + case tree : Super => ; + case _ => ; + if (tree0 != EmptyTree) + System.err.println("BAIL: " + tree0.pos + " " + tree0 + " " + tree0.getClass()); + } + } + def buildUse(term : Symbol, pos : Int) = buildSym(term, pos, false); + def buildDef(term : Symbol, pos : Int) = buildSym(term, pos, true); + + def buildSym(term : Symbol, pos : Int, isDef : Boolean) : Unit = if (term.hasFlag(Flags.ACCESSOR)) buildSym(term.accessed, pos, isDef) + else if (pos == Position.NOPOS) { + System.err.println("NOPOS: " + term); + Thread.dumpStack(); + } else if (term != NoSymbol) { + val name = NameTransformer.decode(term.name.toString()).toString().trim(); + val buf = unit.source.content; + val cs = name.toChars; + var idx = 0; + if (cs.length + pos > buf.length) { + return; + } + else while (idx < cs.length) + if (buf(pos + idx) != cs(idx)) { + // System.err.println("BAD SYM: " + term + " " + term.getClass()); + return; + } + else idx = idx + 1; + + list.put(pos, (if (isDef) new Def(term) else new Use(term))); + } + def selectPos(tree : Select): Int = if (tree.pos == Position.NOPOS) Position.NOPOS else { + val buf = unit.source.content; + if (tree.pos >= buf.length) { + System.err.println(""+tree + "@" + tree.pos + " not in " + unit.source.file.getName() + "[" + buf.length + "]"); + Thread.dumpStack(); + throw new Error(); + } + + val pos = + if (buf(tree.pos) != '.') tree.pos; + else { + def f(x : Int) : Int = { + if (Character.isWhitespace(buf(x))) f(x + 1); + else x; + } + f(tree.pos + 1); + } + pos; + }; + class TokenList { + object begin extends HasNext { + def prev = this; + def length = 0; + } + object end extends HasPrev { + def next = this; + def length = 0; + } + // initialize + begin.next0 = end; + end.prev0 = begin; + + + def tokenAt(offset : Int) = { + //System.err.println("CURSOR-0: " + cursor.offset); + cursor.seek(offset); + //System.err.println("CURSOR-1: " + cursor.offset + " " + cursor.token + " " + offset); + + if (cursor.token.isInstanceOf[Semantic]) cursor.token.asInstanceOf[Semantic]; + else null; + }; + def put(offset : Int, tok : Actual) : Unit = tok match { + case tok0 : Semantic => put(offset, tok0); + case gap : Gap => ; + } + + + def put(offset : Int, tok : Semantic) :Unit = { + cursor.seek(offset); + if (cursor.token == end) { + assert(offset >= cursor.offset); + if (offset > cursor.offset) { + // add a gap. + val gap = new Gap(end.prev); + gap.setLength(offset - cursor.offset); + cursor.offset = offset; + } + // append. + tok.insert(end.prev); + cursor.offset = cursor.offset + tok.length; + + } else if (!cursor.token.isInstanceOf[Gap]) { + val sem = cursor.token.asInstanceOf[Semantic]; + if (sem.symbol == tok.symbol) return; + if (sem.symbol != tok.symbol && + sem.symbol.getClass() == tok.symbol.getClass() && + sem.symbol.pos == tok.symbol.pos) return; + + System.err.println("NOT_GAP: " + sem.symbol + " " + sem.symbol.getClass() + " " + unit.source.dbg(sem.symbol.pos) + " " + sem.symbol.flags); + System.err.println("NOT_GAP: " + tok.symbol + " " + tok.symbol.getClass() + " " + unit.source.dbg(tok.symbol.pos) + " " + tok.symbol.flags); + System.err.println("LIST: " + this); + System.err.println("POS: " + unit.source.dbg(offset)); + + + Thread.dumpStack(); + throw new Error(); + } else { + val gap = cursor.token.asInstanceOf[Gap]; + if (!(offset - cursor.offset + tok.length <= gap.length)) { + System.err.println("LIST =" + this); + System.err.println("OFFSET=" + offset + " " + tok + " " + tok.length); + System.err.println(" " + cursor.offset + " " + gap.length); + throw new Error(); + } + if (offset == cursor.offset) { + // replace or prepend + tok.prev0 = gap.prev0; + if (tok.length == gap.length) { // replace gap + tok.next0 = gap.next0; + } else { + gap.setLength(gap.length - tok.length); + tok.next0 = gap; + } + tok.next0.prev0 = tok; + tok.prev0.next0 = tok; + cursor.token = tok; + } else { + // append + val diff = (cursor.offset + gap.length) - (offset + tok.length); + + gap.setLength(gap.length - tok.length - diff); + tok.prev0 = gap; + tok.next0 = gap.next; + tok.next0.prev0 = tok; + tok.prev0.next0 = tok; + if (diff != 0) { + val gap0 = new Gap(tok); + gap0.setLength(diff); + } + } + } + // System.err.println("PUT: " + this); + } + override def toString() = { + var node = begin.next; + var str = ""; + while (node != end) { + str = str + " " + node; + node = node.next; + } + str; + }; + object cursor { + var token : Token = end; + var offset : Int = 0; + + def next : Unit = if (token == end) end else { + offset = offset + token.length; + token = token.next; + } + def prev : Unit = if (token.prev == begin) token else { + offset = offset - token.prev.length; + token = token.prev; + } + def seek(soffset : Int) : Unit = if (soffset == 0) { + token = begin.next; + offset = 0; + } else { + assert(soffset > 0); + while (offset > soffset) prev; + while (offset + token.length <= soffset && token != end) { + val len0 = offset; + next; + } + } + def convertToGap = if (token.isInstanceOf[Actual]) { + val ret = token.asInstanceOf[Actual].convertToGap; + offset = offset - ret._1; + token = ret._2; + } + } + // add or delete characters + def adjust(offset: Int, /* where */ + length: Int, /* how many characters are modified */ + to : Int /* length of new string */) = { + cursor.seek(offset); + if (cursor.token != end) { + cursor.convertToGap; + while (cursor.offset + cursor.token.length < offset + length && cursor.token.next != end) { + val save = cursor.offset; + cursor.next; + cursor.convertToGap; + assert(cursor.offset == save); + } + if (length != to && cursor.token != end) { + val diff = to - length; + val gap = cursor.token.asInstanceOf[Gap]; + gap.setLength(gap.length + diff); + }; + } + } + }; + } +} + diff --git a/src/compiler/scala/tools/nsc/models/Signatures.scala b/src/compiler/scala/tools/nsc/models/Signatures.scala new file mode 100644 index 0000000000..3a779a7021 --- /dev/null +++ b/src/compiler/scala/tools/nsc/models/Signatures.scala @@ -0,0 +1,65 @@ + +package scala.tools.nsc.models; + +import scala.tools.nsc.{Global => Compiler}; +import scala.tools.nsc.symtab.{Flags,Names}; +import scala.tools.nsc.util.{NameTransformer,Position,SourceFile}; +import scala.collection.mutable.{HashMap,HashSet}; + +class Signatures(val compiler: Compiler) { + import compiler._; + + class Signature(val name : String, val children : List[Signature]) { + def asString : String = name + "[" + asString0(children) + "]"; + } + def sort(sigs : List[Signature]) = + sigs.sort((l0,l1) => l0.name.compareTo(l1.name) > 0); + + def asString0(sigs : List[Signature]) : String = { + var ret = ""; + for (val sig <- sort(sigs)) + ret = ret + sig.asString; + ret; + } + + def signature(unit : CompilationUnit): String = asString0(signature(unit.body, Nil)); + + def signature(trees : List[Tree]) : List[Signature] = { + var ret : List[Signature] = Nil; + for (val tree <- trees) ret = signature(tree, ret); + ret; + } + def signature(tree0 : Tree, rest : List[Signature]) : List[Signature] = tree0 match { + case tree : MemberDef => if (!tree.mods.isPrivate) { + val name = "" + tree.name + "::" + tree.mods; + val children : List[Signature] = (tree match { + case impl : ImplDef => + val supers = new Signature("$$supers", signature(impl.impl.parents)); + val body = new Signature("$$body", signature(impl.impl.body)); + val ret = supers :: body :: Nil; + (impl match { + case cdef : ClassDef => new Signature("$$tparams", signature(cdef.tparams)) :: ret; + case _ => ret; + }); + case vdef : ValOrDefDef => + val ret = signature(vdef.tpt, Nil); + (vdef match { + case ddef : DefDef => + val tparams = new Signature("$$tparams", signature(ddef.tparams)); + var vparamss : List[Signature] = Nil; + for (val list <- ddef.vparamss) vparamss = signature(list) ::: vparamss; + tparams :: vparamss; + case _ => ret; + }); + case pdef : PackageDef => signature(pdef.stats); + case _ => Nil; + }); + new Signature(name, children) :: rest; + + } else rest; + case tree : TypeTree => new Signature("" + tree.tpe, Nil) :: rest; + case _ => rest; + } + + +} diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala new file mode 100644 index 0000000000..c2953e0a5c --- /dev/null +++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala @@ -0,0 +1,55 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scala.tools.nsc.reporters; +import scala.collection.mutable.HashSet; +import scala.tools.nsc.util.Position; + +/** + * This reporter implements filtering. + */ +abstract class AbstractReporter extends Reporter { + private val positions = new HashSet[Position](); + + def display(pos : Position, msg : String, severity : Severity) : Unit; + var prompt : Boolean = false; + var verbose : Boolean = false; + var nowarn : Boolean = false; + def displayPrompt : Unit; + + // XXX: while is pos ignored? + protected def info0(pos : Position, msg : String, severity : Severity, force : Boolean) : Unit = severity match { + case INFO => if (force || verbose) display(pos, msg, severity); + case WARNING => { + val hidden = testAndLog(pos); + if (!nowarn) { + if (!hidden || prompt) display(pos, msg, severity); + if (prompt) displayPrompt; + } + } + case ERROR => { + val hidden = testAndLog(pos); + if (!hidden || prompt) display(pos, msg, severity); + if (prompt) displayPrompt; + } + } + + //######################################################################## + // Private Methods + + /** Logs a position and returns true if it was already logged. */ + private def testAndLog(pos : Position) : Boolean = { + if (pos == null) return false; + if (pos.column == 0) return false; + if (positions.contains(pos)) return true; + positions += (pos); + return false; + } + + //######################################################################## +} diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala new file mode 100644 index 0000000000..f759b66bec --- /dev/null +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -0,0 +1,133 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scala.tools.nsc.reporters; +import scala.tools.nsc.util.Position; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * This class implements a Reporter that displays messages on a text + * console. + */ +class ConsoleReporter(reader : BufferedReader, writer : PrintWriter) extends AbstractReporter { + //######################################################################## + // Public Fields + + /** Whether a short file name should be displayed before errors */ + var shortname : Boolean = false; + + def label(severity : Severity): String = severity match { + case ERROR => "error"; + case WARNING => "warning"; + case INFO => null; + }; + def clabel(severity : Severity) = { + val label0 = label(severity); + if (label0 == null) ""; else label0 + ": "; + } + + + + //######################################################################## + // Public Constructors + def this() = this( + new BufferedReader(new InputStreamReader(System.in)), + new PrintWriter(System.err, true)); + + //######################################################################## + // Public Methods - Count + + + /** Returns the number of errors issued totally as a string */ + def getCountString(severity : Severity) : String = getCountString0(count(severity), label(severity));; + /** Returns a string meaning "n elements". */ + def getCountString0(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"; + } + + + //######################################################################## + // Public Methods - Print + + /** Prints the message. */ + def printMessage(msg : String) = writer.println(msg); + + /** Prints the message with the given position indication. */ + def printMessage(pos : Position, msg : String) : Unit = { + if (pos != null) { + val buf = new StringBuffer(msg); + buf.insert(0, " "); + if (pos.line != Position.NOLINE) + buf.insert(0, ":" + pos.line); + val file = pos.source.file; + buf.insert(0, if (shortname) file.getName() else file.getPath()); + printMessage(buf.toString()); + printSourceLine(pos); + } else printMessage(msg); + } + + def print(pos : Position, msg : String, severity : Severity) = printMessage(pos, clabel(severity) + msg); + + def printSourceLine(pos : Position) = if (pos != null && pos.offset != Position.NOPOS) { + printMessage(pos.lineContent); + printColumnMarker(pos); + } + /** Prints the column marker of the given position. */ + def printColumnMarker(pos : Position) = if (pos != null) { + val buffer = new StringBuffer(pos.column); + var i = 1; + while (i < pos.column) { + buffer.append(' '); + i = i + 1; + } + if (pos.column > 0) buffer.append('^'); + printMessage(buffer.toString()); + } + + /** Prints the number of errors and warnings if their are non-zero. */ + def printSummary() = { + if (warnings > 0) printMessage(getCountString(WARNING) + " found"); + if ( errors > 0) printMessage(getCountString(ERROR ) + " found"); + } + + //######################################################################## + // Public Methods - Display + def display(pos : Position, msg : String, severity : Severity) : Unit = { + incr(severity); + print(pos, msg, severity); + }; + def displayPrompt : Unit = try { + var continue = true; + while (continue) { + writer.print("r)esume, a)bort: "); + writer.flush(); + var line = reader.readLine(); + if (line != null) { + line = line.toLowerCase(); + if ("abort".startsWith(line)) + throw new Error("user abort"); + if ("resume".startsWith(line)) continue = false; + } + } + } catch { + case ex: IOException => { + ex.printStackTrace(); + throw new Error("input read error"); + } + } +} diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala new file mode 100644 index 0000000000..950e1a2c6f --- /dev/null +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -0,0 +1,46 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scala.tools.nsc.reporters; +import scala.tools.nsc.util.Position; + + +/** + * This interface provides methods to issue information, warning and + * error messages. + */ +abstract class Reporter { + abstract class Severity(val code : Int); + object INFO extends Severity(0); + object WARNING extends Severity(1); + object ERROR extends Severity(2); + + def reset : Unit = { + errors = 0; + warnings = 0; + + } + def count(severity : Severity): Int = severity match { + case ERROR => errors; + case WARNING => warnings; + case INFO => 0; + }; + def incr(severity : Severity): Unit = severity match { + case ERROR => errors = errors + 1; + case WARNING => warnings = warnings + 1;; + case INFO => {} + }; + var errors : Int = 0; + var warnings : Int = 0; + + protected def info0(pos : Position, msg : String, severity : Severity, force : Boolean) : Unit; + + def info(pos : Position, msg : String, force : Boolean) : Unit = info0(pos, msg, INFO , force); + def warning(pos : Position, msg : String ) : Unit = info0(pos, msg, WARNING, false); + def error(pos : Position, msg : String ) : Unit = info0(pos, msg, ERROR, false); +} diff --git a/src/compiler/scala/tools/nsc/reporters/ReporterTimer.scala b/src/compiler/scala/tools/nsc/reporters/ReporterTimer.scala new file mode 100644 index 0000000000..5c0cfd4092 --- /dev/null +++ b/src/compiler/scala/tools/nsc/reporters/ReporterTimer.scala @@ -0,0 +1,27 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scala.tools.nsc.reporters; +import scala.tools.util.AbstractTimer; + +/** + * This class implements a timer that uses a Reporter to issue + * timings. + */ +class ReporterTimer(_reporter : Reporter) extends AbstractTimer { + //######################################################################## + // Private Fields + private val reporter = _reporter; + //######################################################################## + // Public Methods + + def issue(msg : String, duration : Long) = + reporter.info(null, "[" + msg + " in " + duration + "ms]", false); + + //######################################################################## +} diff --git a/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala b/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala new file mode 100644 index 0000000000..e4734fcee7 --- /dev/null +++ b/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala @@ -0,0 +1,38 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scala.tools.nsc.reporters; +import scala.tools.nsc.util.Position; +import scala.collection.mutable.HashSet; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * This class implements a Reporter that displays messages on a text + * console. + */ +class StoreReporter extends Reporter { + + class Info(val pos: Position, val msg: String, val severity: Severity) { + override def toString() = "pos: " + pos + " " + msg + " " + severity; + } + + val infos = new HashSet[Info]; + + protected def info0(pos : Position, msg : String, severity : Severity, force : Boolean) : Unit = if (!force) { + infos += new Info(pos, msg, severity); + incr(severity); + } + override def reset = { + super.reset; + infos.clear; + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/Constants.scala b/src/compiler/scala/tools/nsc/symtab/Constants.scala new file mode 100644 index 0000000000..3a6746cf61 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/Constants.scala @@ -0,0 +1,191 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.symtab; + +import classfile.PickleFormat._; + +[_trait_] abstract class Constants: SymbolTable { + + import definitions._; + + final val UnitTag = LITERALunit - LITERAL; + final val BooleanTag = LITERALboolean - LITERAL; + final val ByteTag = LITERALbyte - LITERAL; + final val ShortTag = LITERALshort - LITERAL; + final val CharTag = LITERALchar - LITERAL; + final val IntTag = LITERALint - LITERAL; + final val LongTag = LITERALlong - LITERAL; + final val FloatTag = LITERALfloat - LITERAL; + final val DoubleTag = LITERALdouble - LITERAL; + final val StringTag = LITERALstring - LITERAL; + final val NullTag = LITERALnull - LITERAL; + final val ZeroTag = LITERALzero - LITERAL; + + case class Constant(value: Any) { + val tag: int = + if (value.isInstanceOf[unit]) UnitTag + else if (value.isInstanceOf[boolean]) BooleanTag + else if (value.isInstanceOf[byte]) ByteTag + else if (value.isInstanceOf[short]) ShortTag + else if (value.isInstanceOf[char]) CharTag + else if (value.isInstanceOf[int]) IntTag + else if (value.isInstanceOf[long]) LongTag + else if (value.isInstanceOf[float]) FloatTag + else if (value.isInstanceOf[double]) DoubleTag + else if (value.isInstanceOf[String]) StringTag + else if (value == null) NullTag + else throw new Error("bad constant value: " + value); + + def tpe: Type = tag match { + case UnitTag => UnitClass.tpe + case BooleanTag => BooleanClass.tpe + case ByteTag => ByteClass.tpe + case ShortTag => ShortClass.tpe + case CharTag => CharClass.tpe + case IntTag => IntClass.tpe + case LongTag => LongClass.tpe + case FloatTag => FloatClass.tpe + case DoubleTag => DoubleClass.tpe + case StringTag => StringClass.tpe + case NullTag => AllRefClass.tpe + } + + /** 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.value == that.value && this.tag == that.tag + case _ => false + } + + def booleanValue: boolean = + if (tag == BooleanTag) value.asInstanceOf$erased[boolean] + else throw new Error("value " + value + " is not a boolean"); + + def byteValue: byte = tag match { + case ByteTag => value.asInstanceOf$erased[byte] + case ShortTag => value.asInstanceOf$erased[short].asInstanceOf[byte] + case CharTag => value.asInstanceOf$erased[char].asInstanceOf[byte] + case IntTag => value.asInstanceOf$erased[int].asInstanceOf[byte] + case LongTag => value.asInstanceOf$erased[long].asInstanceOf[byte] + case FloatTag => value.asInstanceOf$erased[float].asInstanceOf[byte] + case DoubleTag => value.asInstanceOf$erased[double].asInstanceOf[byte] + case _ => throw new Error("value " + value + " is not a byte") + } + + def shortValue: short = tag match { + case ByteTag => value.asInstanceOf$erased[byte].asInstanceOf[short] + case ShortTag => value.asInstanceOf$erased[short] + case CharTag => value.asInstanceOf$erased[char].asInstanceOf[short] + case IntTag => value.asInstanceOf$erased[int].asInstanceOf[short] + case LongTag => value.asInstanceOf$erased[long].asInstanceOf[short] + case FloatTag => value.asInstanceOf$erased[float].asInstanceOf[short] + case DoubleTag => value.asInstanceOf$erased[double].asInstanceOf[short] + case _ => throw new Error("value " + value + " is not a short") + } + + def charValue: char = tag match { + case ByteTag => value.asInstanceOf$erased[byte].asInstanceOf[char] + case ShortTag => value.asInstanceOf$erased[short].asInstanceOf[char] + case CharTag => value.asInstanceOf$erased[char] + case IntTag => value.asInstanceOf$erased[int].asInstanceOf[char] + case LongTag => value.asInstanceOf$erased[long].asInstanceOf[char] + case FloatTag => value.asInstanceOf$erased[float].asInstanceOf[char] + case DoubleTag => value.asInstanceOf$erased[double].asInstanceOf[char] + case _ => throw new Error("value " + value + " is not a char") + } + + def intValue: int = tag match { + case ByteTag => value.asInstanceOf$erased[byte].asInstanceOf[int] + case ShortTag => value.asInstanceOf$erased[short].asInstanceOf[int] + case CharTag => value.asInstanceOf$erased[char].asInstanceOf[int] + case IntTag => value.asInstanceOf$erased[int] + case LongTag => value.asInstanceOf$erased[long].asInstanceOf[int] + case FloatTag => value.asInstanceOf$erased[float].asInstanceOf[int] + case DoubleTag => value.asInstanceOf$erased[double].asInstanceOf[int] + case _ => throw new Error("value " + value + " is not an int") + } + + def longValue: long = tag match { + case ByteTag => value.asInstanceOf$erased[byte].asInstanceOf[long] + case ShortTag => value.asInstanceOf$erased[short].asInstanceOf[long] + case CharTag => value.asInstanceOf$erased[char].asInstanceOf[long] + case IntTag => value.asInstanceOf$erased[int].asInstanceOf[long] + case LongTag => value.asInstanceOf$erased[long] + case FloatTag => value.asInstanceOf$erased[float].asInstanceOf[long] + case DoubleTag => value.asInstanceOf$erased[double].asInstanceOf[long] + case _ => throw new Error("value " + value + " is not a long") + } + + def floatValue: float = tag match { + case ByteTag => value.asInstanceOf$erased[byte].asInstanceOf[float] + case ShortTag => value.asInstanceOf$erased[short].asInstanceOf[float] + case CharTag => value.asInstanceOf$erased[char].asInstanceOf[float] + case IntTag => value.asInstanceOf$erased[int].asInstanceOf[float] + case LongTag => value.asInstanceOf$erased[long].asInstanceOf[float] + case FloatTag => value.asInstanceOf$erased[float] + case DoubleTag => value.asInstanceOf$erased[double].asInstanceOf[float] + case _ => throw new Error("value " + value + " is not a float") + } +/* + def doubleValue: double = { + System.out.println("doubleValue " + tag + " " + value); + tag match { + case ByteTag => System.out.println("Byte"); value.asInstanceOf$erased[byte].asInstanceOf[double] + case ShortTag => System.out.println("Short"); value.asInstanceOf$erased[short].asInstanceOf[double] + case CharTag => System.out.println("Char"); value.asInstanceOf$erased[char].asInstanceOf[double] + case IntTag => System.out.println("Int"); value.asInstanceOf$erased[int].asInstanceOf[double] + case LongTag => System.out.println("Long"); value.asInstanceOf$erased[long].asInstanceOf[double] + case FloatTag => System.out.println("Float"); value.asInstanceOf$erased[float].asInstanceOf[double] + case DoubleTag => System.out.println("Double"); value.asInstanceOf$erased[double] + case _ => System.out.println("error"); throw new Error("value " + value + " is not a double") + } + } +*/ + def doubleValue: double = tag match { + case ByteTag => value.asInstanceOf$erased[byte].asInstanceOf[double] + case ShortTag => value.asInstanceOf$erased[short].asInstanceOf[double] + case CharTag => value.asInstanceOf$erased[char].asInstanceOf[double] + case IntTag => value.asInstanceOf$erased[int].asInstanceOf[double] + case LongTag => value.asInstanceOf$erased[long].asInstanceOf[double] + case FloatTag => value.asInstanceOf$erased[float].asInstanceOf[double] + case DoubleTag => value.asInstanceOf$erased[double] + case _ => throw new Error("value " + value + " is not a double") + } + + /** Convert constant value to conform to given type */ + def convertTo(pt: Type): Constant = { + val target = pt.symbol; + if (target == tpe.symbol) + this + else if (target == ByteClass && ByteTag <= tag && tag <= IntTag && + -128 <= intValue && intValue <= 127) + Constant(byteValue) + else if (target == ShortClass && ByteTag <= tag && tag <= IntTag && + -32768 <= intValue && intValue <= 32767) + Constant(shortValue) + else if (target == CharClass && ByteTag <= tag && tag <= IntTag && + 0 <= intValue && intValue <= 65635) + Constant(charValue) + else if (target == IntClass && ByteTag <= tag && tag <= IntTag) + Constant(intValue) + else if (target == LongClass && ByteTag <= tag && tag <= LongTag) + Constant(longValue) + else if (target == FloatClass && ByteTag <= tag && tag <= FloatTag) + Constant(floatValue) + else if (target == DoubleClass && ByteTag <= tag && tag <= DoubleTag) + Constant(doubleValue) + else null + } + + def stringValue: String = + if (value == null) "null" else value.toString(); + + override def hashCode(): int = + if (value == null) 0 else value.hashCode() * 41 + 17; + } + +} diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala new file mode 100644 index 0000000000..36d34f76f1 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -0,0 +1,417 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +import scala.tools.nsc.util.Position; +import collection.mutable.HashMap; +import Flags._; + +[_trait_] abstract class Definitions: SymbolTable { + + object definitions { + + // root packages and classes + var RootClass: Symbol = _; + var EmptyPackage: Symbol = _; + var EmptyPackageClass: Symbol = _; + var emptypackagescope: Scope = null; //debug + + var JavaPackage: Symbol = _; + var JavaLangPackage: Symbol = _; + var ScalaPackage: Symbol = _; + var ScalaPackageClass: Symbol = _; + + var AnyClass: Symbol = _; + var AnyValClass: Symbol = _; + var ObjectClass: Symbol = _; + + var AnyRefClass: Symbol = _; + + var AllRefClass: Symbol = _; + var AllClass: Symbol = _; + + var StringClass: Symbol = _; + var ThrowableClass: Symbol = _; + + // the scala value classes + var UnitClass: Symbol = _; + var BooleanClass: Symbol = _; + def Boolean_not = getMember(BooleanClass, nme.ZNOT); + def Boolean_and = getMember(BooleanClass, nme.ZAND); + def Boolean_or = getMember(BooleanClass, nme.ZOR); + var ByteClass: Symbol = _; + var ShortClass: Symbol = _; + var CharClass: Symbol = _; + var IntClass: Symbol = _; + var LongClass: Symbol = _; + var FloatClass: Symbol = _; + var DoubleClass: Symbol = _; + + // the scala reference classes + var ScalaObjectClass: Symbol = _; + def ScalaObjectClass_tag = getMember(ScalaObjectClass, nme.tag ); + var AttributeClass: Symbol = _; + var RefClass: Symbol = _; + var TypedCodeClass: Symbol = _; + var PartialFunctionClass: Symbol = _; + var IterableClass: Symbol = _; + def Iterable_next = getMember(IterableClass, "next"); + def Iterable_hasNext = getMember(IterableClass, "hasNext"); + var IteratorClass: Symbol = _; + var SeqClass: Symbol = _; + def Seq_length = getMember(SeqClass, "length"); + var ListClass: Symbol = _; + def List_isEmpty = getMember(ListClass, "isEmpty"); + def List_head = getMember(ListClass, "head"); + def List_tail = getMember(ListClass, "tail"); + var ArrayClass: Symbol = _; + var TypeClass: Symbol = _; + var SerializableClass: Symbol = _; + var PredefModule: Symbol = _; + var ConsoleModule: Symbol = _; + var MatchErrorClass: Symbol = _; + var MatchErrorModule: Symbol = _; + def MatchError_fail = getMember(MatchErrorModule, "fail"); + def MatchError_report = getMember(MatchErrorModule, "report"); + var ScalaRunTimeModule: Symbol = _; + def SeqFactory = getMember(ScalaRunTimeModule, "Seq"); + var RepeatedParamClass: Symbol = _; + var ByNameParamClass: Symbol = _; + var TraitClass: Symbol = _; + + val MaxTupleArity = 9; + val MaxFunctionArity = 9; + var TupleClass: Array[Symbol] = _; + def tupleField(n:int, j:int) = getMember(TupleClass(n), "_" + j); + var FunctionClass: Array[Symbol] = _; + def functionApply(n:int) = getMember(FunctionClass(n), "apply"); + + def tupleType(elems: List[Type]) = + if (elems.length <= MaxTupleArity) { + val sym = TupleClass(elems.length); + typeRef(sym.typeConstructor.prefix, sym, elems) + } else NoType; + + + def functionType(formals: List[Type], restpe: Type) = + if (formals.length <= MaxFunctionArity) { + val sym = FunctionClass(formals.length); + typeRef(sym.typeConstructor.prefix, sym, formals ::: List(restpe)) + } else NoType; + + + def isTupleType(tp: Type): boolean = tp match { + case TypeRef(_, sym, elems) => + elems.length <= MaxTupleArity && sym == TupleClass(elems.length); + case _ => + false + } + + def isFunctionType(tp: Type): boolean = tp match { + case TypeRef(_, sym, args) => + ((args.length > 0) && (args.length - 1 <= MaxFunctionArity) && + (sym == FunctionClass(args.length - 1))) + case _ => + false + } + + def seqType(arg: Type) = + typeRef(SeqClass.typeConstructor.prefix, SeqClass, List(arg)); + + def NilModule: Symbol = getModule("scala.Nil"); + def ConsClass: Symbol = getClass("scala.$colon$colon"); + + // members of class scala.Any + var Any_== : Symbol = _; + var Any_!= : Symbol = _; + var Any_equals : Symbol = _; + var Any_hashCode : Symbol = _; + var Any_toString : Symbol = _; + var Any_isInstanceOf: Symbol = _; + var Any_asInstanceOf: Symbol = _; + var Any_isInstanceOfErased: Symbol = _; + var Any_asInstanceOfErased: Symbol = _; + + // members of class java.lang.{Object, String} + var Object_eq : Symbol = _; + var Object_ne : Symbol = _; + var Object_== : Symbol = _; + var Object_!= : Symbol = _; + var Object_synchronized: Symbol = _; + var Object_isInstanceOf: Symbol = _; + var Object_asInstanceOf: Symbol = _; + def Object_equals = getMember(ObjectClass, nme.equals_); + def Object_hashCode = getMember(ObjectClass, nme.hashCode_); + def Object_toString = getMember(ObjectClass, nme.toString_); + + var String_+ : Symbol = _; + + // members of class scala.Iterator + var Iterator_next : Symbol = _; + var Iterator_hasNext : Symbol = _; + + // pattern wildcard + var PatternWildcard: Symbol = _; + + // boxed classes + var BoxedArrayClass: Symbol = _; + var BoxedAnyArrayClass: Symbol = _; + var BoxedObjectArrayClass: Symbol = _; + var BoxedNumberClass: Symbol = _; + var BoxedUnitClass: Symbol = _; + var BoxedUnitModule: Symbol = _; + def BoxedUnit_UNIT = getMember(BoxedUnitModule, "UNIT"); + var ObjectRefClass: Symbol = _; + + // special attributes + var SerializableAttr: Type = _; + + + def getModule(fullname: Name): Symbol = + getModuleOrClass(fullname, true); + + def getClass(fullname: Name): Symbol = + getModuleOrClass(fullname, false); + + def getMember(owner: Symbol, name: Name) = { + val result = owner.info.nonPrivateMember(name); + if (result == NoSymbol) + throw new FatalError(owner.toString() + " does not have a member " + name); + result + } + + private def getModuleOrClass(fullname: Name, module: boolean): Symbol = { + var sym = RootClass; + var i = 0; + var j = fullname.pos('.', i); + while (j < fullname.length) { + sym = sym.info.nonPrivateMember(fullname.subName(i, j)); + i = j + 1; + j = fullname.pos('.', i) + } + val result = + if (module) sym.info.nonPrivateMember(fullname.subName(i, j)).suchThat(.hasFlag(MODULE)); + else sym.info.nonPrivateMember(fullname.subName(i, j).toTypeName); + if (result == NoSymbol) + throw new FatalError((if (module) "object " else "class ") + fullname + " not found."); + result + } + + private def newClass(owner: Symbol, name: Name, parents: List[Type]): Symbol = { + val clazz = owner.newClass(Position.NOPOS, name.toTypeName); + clazz.setInfo(ClassInfoType(parents, new Scope(), clazz)); + owner.info.decls.enter(clazz); + clazz + } + + private def newCovariantPolyClass(owner: Symbol, name: Name, parent: Symbol => Type): Symbol = { + val clazz = newClass(owner, name, List()); + val tparam = newTypeParam(clazz, 0) setFlag COVARIANT; + clazz.setInfo( + PolyType( + List(tparam), + ClassInfoType(List(parent(tparam)), new Scope(), clazz))) + } + + private def newAlias(owner: Symbol, name: Name, alias: Type): Symbol = { + val tpsym = owner.newAliasType(Position.NOPOS, name.toTypeName); + tpsym.setInfo(alias); + owner.info.decls.enter(tpsym); + tpsym + } + + private def newMethod(owner: Symbol, name: Name): Symbol = { + val msym = owner.newMethod(Position.NOPOS, name.encode); + owner.info.decls.enter(msym); + msym + } + + private def newMethod(owner: Symbol, name: Name, formals: List[Type], restpe: Type): Symbol = + newMethod(owner, name).setInfo(MethodType(formals, restpe)); + + private def newPolyMethod(owner: Symbol, name: Name, tcon: Symbol => Type): Symbol = { + val msym = newMethod(owner, name); + val tparam = newTypeParam(msym, 0); + msym.setInfo(PolyType(List(tparam), tcon(tparam))) + } + + private def newTypeParam(owner: Symbol, index: int): Symbol = + owner.newTypeParameter(Position.NOPOS, "T" + index) + .setInfo(TypeBounds(AllClass.typeConstructor, AnyClass.typeConstructor)); + + val boxedClass = new HashMap[Symbol, Symbol]; + val boxedArrayClass = new HashMap[Symbol, Symbol]; + val refClass = new HashMap[Symbol, Symbol]; + private val abbrvTag = new HashMap[Symbol, char]; + + private def getValueClass(name: String, tag: char): Symbol = { + val result = getClass("scala." + name); + boxedClass(result) = getClass("scala.runtime.Boxed" + name); + if (name != "Unit") { + boxedArrayClass(result) = getClass("scala.runtime.Boxed" + name + "Array"); + refClass(result) = getClass("scala.runtime." + name + "Ref"); + } + abbrvTag(result) = tag; + result + } + + /** Is symbol a value class? */ + def isValueClass(sym: Symbol): boolean = boxedClass contains sym; + + /** Is symbol a value class? */ + def isNumericValueClass(sym: Symbol): boolean = + isValueClass(sym) && sym != BooleanClass && sym != UnitClass; + + /** Is symbol a value or array class? */ + def isUnboxedClass(sym: Symbol): boolean = isValueClass(sym) || sym == ArrayClass; + + def signature(tp: Type): String = { + def flatNameString(sym: Symbol, separator: char): String = + if (sym.owner.isPackageClass) sym.fullNameString('.') + else flatNameString(sym.owner, separator) + "$" + sym.simpleName; + def signature1(tp: Type): String = { + if (tp.symbol == ArrayClass) "[" + signature1(tp.typeArgs.head); + else if (isValueClass(tp.symbol)) String.valueOf(abbrvTag(tp.symbol)) + else "L" + flatNameString(tp.symbol, '/') + ";" + } + if (tp.symbol == ArrayClass) signature1(tp); + else flatNameString(tp.symbol, '.') + } + + private var isInitialized = false; + + def init: unit = { + if (isInitialized) return; + isInitialized = true; + RootClass = + NoSymbol.newClass(Position.NOPOS, nme.ROOT.toTypeName) + .setFlag(FINAL | MODULE | PACKAGE | JAVA).setInfo(rootLoader); + + EmptyPackage = + RootClass.newPackage(Position.NOPOS, nme.EMPTY_PACKAGE_NAME).setFlag(FINAL); + EmptyPackageClass = EmptyPackage.moduleClass; + EmptyPackageClass.setInfo(ClassInfoType(List(), new Scope(), EmptyPackageClass)); + + EmptyPackage.setInfo(EmptyPackageClass.tpe); + RootClass.info.decls.enter(EmptyPackage); + + JavaPackage = getModule("java"); + JavaLangPackage = getModule("java.lang"); + ScalaPackage = getModule("scala"); + ScalaPackageClass = ScalaPackage.tpe.symbol; + + AnyClass = newClass(ScalaPackageClass, "Any", List()); + AnyValClass = getClass("scala.AnyVal") setFlag SEALED; + ObjectClass = getClass("java.lang.Object"); + + AnyRefClass = newAlias(ScalaPackageClass, "AnyRef", ObjectClass.typeConstructor); + + AllRefClass = newClass(ScalaPackageClass, "AllRef", List(AnyRefClass.typeConstructor)) + .setFlag(ABSTRACT | TRAIT | FINAL); + + AllClass = newClass(ScalaPackageClass, "All", List(AnyClass.typeConstructor)) + .setFlag(ABSTRACT | TRAIT | FINAL); + + StringClass = getClass("java.lang.String"); + ThrowableClass = getClass("java.lang.Throwable"); + + // the scala value classes + UnitClass = getValueClass("Unit", 'V'); + BooleanClass = getValueClass("Boolean", 'Z'); + ByteClass = getValueClass("Byte", 'B'); + ShortClass = getValueClass("Short", 'S'); + CharClass = getValueClass("Char", 'C'); + IntClass = getValueClass("Int", 'I'); + LongClass = getValueClass("Long", 'L'); + FloatClass = getValueClass("Float", 'F'); + DoubleClass = getValueClass("Double", 'D'); + + // the scala reference classes + ScalaObjectClass = getClass("scala.ScalaObject"); + AttributeClass = getClass("scala.Attribute"); + RefClass = getClass("scala.Ref"); + TypedCodeClass = getClass("scala.reflect.TypedCode"); + PartialFunctionClass = getClass("scala.PartialFunction"); + IterableClass = getClass("scala.Iterable"); + IteratorClass = getClass("scala.Iterator"); + SeqClass = getClass("scala.Seq"); + ListClass = getClass("scala.List"); + ArrayClass = getClass("scala.Array"); + TypeClass = getClass("scala.Type"); + SerializableClass = getClass("java.io.Serializable"); + PredefModule = getModule("scala.Predef"); + ConsoleModule = getModule("scala.Console"); + MatchErrorClass = getClass("scala.MatchError"); + MatchErrorModule = getModule("scala.MatchError"); + ScalaRunTimeModule = getModule("scala.runtime.ScalaRunTime"); + RepeatedParamClass = newCovariantPolyClass( + ScalaPackageClass, nme.REPEATED_PARAM_CLASS_NAME, + tparam => typeRef(SeqClass.typeConstructor.prefix, SeqClass, List(tparam.typeConstructor))); + ByNameParamClass = newCovariantPolyClass( + ScalaPackageClass, nme.BYNAME_PARAM_CLASS_NAME, tparam => AnyClass.typeConstructor); + TraitClass = getClass("scala._trait_"); + TupleClass = new Array(MaxTupleArity + 1); + for (val i <- List.range(1, MaxTupleArity + 1)) + TupleClass(i) = getClass("scala.Tuple" + i); + FunctionClass = new Array(MaxFunctionArity + 1); + for (val i <- List.range(0, MaxFunctionArity + 1)) + FunctionClass(i) = getClass("scala.Function" + i); + + // members of class scala.Any + Any_== = newMethod( + AnyClass, "==", List(AnyClass.typeConstructor), BooleanClass.typeConstructor) setFlag FINAL; + Any_!= = newMethod( + AnyClass, "!=", List(AnyClass.typeConstructor), BooleanClass.typeConstructor) setFlag FINAL; + Any_equals = newMethod( + AnyClass, "equals", List(AnyClass.typeConstructor), BooleanClass.typeConstructor); + Any_hashCode = newMethod( + AnyClass, "hashCode", List(), IntClass.typeConstructor); + Any_toString = newMethod( + AnyClass, "toString", List(), StringClass.typeConstructor); + + Any_isInstanceOf = newPolyMethod( + AnyClass, "isInstanceOf", tparam => BooleanClass.typeConstructor) setFlag FINAL; + Any_asInstanceOf = newPolyMethod( + AnyClass, "asInstanceOf", tparam => tparam.typeConstructor) setFlag FINAL; + Any_isInstanceOfErased = newPolyMethod( + AnyClass, "isInstanceOf$erased", tparam => BooleanClass.typeConstructor) setFlag FINAL; + Any_asInstanceOfErased = newPolyMethod( + AnyClass, "asInstanceOf$erased", tparam => tparam.typeConstructor) setFlag FINAL; + + // members of class java.lang.{Object, String} + Object_== = newMethod( + ObjectClass, "==", List(AnyRefClass.typeConstructor), BooleanClass.typeConstructor) setFlag FINAL; + Object_!= = newMethod( + ObjectClass, "!=", List(AnyRefClass.typeConstructor), BooleanClass.typeConstructor) setFlag FINAL; + Object_eq = newMethod( + ObjectClass, "eq", List(AnyRefClass.typeConstructor), BooleanClass.typeConstructor) setFlag FINAL; + Object_ne = newMethod( + ObjectClass, "ne", List(AnyRefClass.typeConstructor), BooleanClass.typeConstructor) setFlag FINAL; + Object_synchronized = newPolyMethod( + ObjectClass, "synchronized", tparam => MethodType(List(tparam.typeConstructor), tparam.typeConstructor)) setFlag FINAL; + Object_isInstanceOf = newPolyMethod( + ObjectClass, "$isInstanceOf", + tparam => MethodType(List(), BooleanClass.typeConstructor)) setFlag FINAL; + Object_asInstanceOf = newPolyMethod( + ObjectClass, "$asInstanceOf", + tparam => MethodType(List(), tparam.typeConstructor)) setFlag FINAL; + String_+ = newMethod( + StringClass, "+", List(AnyClass.typeConstructor), StringClass.typeConstructor) setFlag FINAL; + + PatternWildcard = NoSymbol.newValue(Position.NOPOS, "_").setInfo(AllClass.typeConstructor); + + BoxedArrayClass = getClass("scala.runtime.BoxedArray"); + BoxedAnyArrayClass = getClass("scala.runtime.BoxedAnyArray"); + BoxedObjectArrayClass = getClass("scala.runtime.BoxedObjectArray"); + BoxedNumberClass = getClass("scala.runtime.BoxedNumber"); + BoxedUnitClass = getClass("scala.runtime.BoxedUnit"); + BoxedUnitModule = getModule("scala.runtime.BoxedUnit"); + ObjectRefClass = getClass("scala.runtime.ObjectRef"); + + SerializableAttr = getClass("scala.serializable").tpe; + } + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/Flags.scala b/src/compiler/scala/tools/nsc/symtab/Flags.scala new file mode 100644 index 0000000000..2627640d2e --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/Flags.scala @@ -0,0 +1,191 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +object Flags { + + // modifiers + final val IMPLICIT = 0x00000001; + final val FINAL = 0x00000002; + final val PRIVATE = 0x00000004; + final val PROTECTED = 0x00000008; + + final val SEALED = 0x00000010; + final val OVERRIDE = 0x00000020; + final val CASE = 0x00000040; + final val ABSTRACT = 0x00000080; // abstract class, or used in conjunction + // with abstract override. + // Note difference to DEFERRED! + + final val DEFERRED = 0x00000100; // was `abstract' for members + final val METHOD = 0x00000200; // a method + final val MODULE = 0x00000400; // symbol is module or class implementing a module + final val INTERFACE = 0x00000800; // symbol is an interface + + final val MUTABLE = 0x00001000; // symbol is a mutable variable. + final val PARAM = 0x00002000; // symbol is a (value or type) parameter to a method + final val PACKAGE = 0x00004000; // symbol is a java package + final val DEPRECATED = 0x00008000; // symbol is deprecated. + + final val COVARIANT = 0x00010000; // symbol is a covariant type variable + final val CONTRAVARIANT = 0x00020000; // symbol is a contravariant type variable + final val ABSOVERRIDE = 0x00040000; // combination of abstract & override + final val LOCAL = 0x00080000; // symbol is local to current class. + // pre: PRIVATE is also set + + final val JAVA = 0x00100000; // symbol was defined by a Java class + final val SYNTHETIC = 0x00200000; // symbol is compiler-generated + final val STABLE = 0x00400000; // functions that are assumed to be stable + // (typically, access methods for valdefs) + final val STATIC = 0x00800000; // static field, method or class + + final val CASEACCESSOR = 0x01000000; // symbol is a case parameter (or its accessor) + final val TRAIT = 0x02000000; // symbol is a trait + final val BRIDGE = 0x04000000; // function is a bridge method. Set by Erasure + final val ACCESSOR = 0x08000000; // a value or variable accessor + + final val SUPERACCESSOR = 0x10000000; // a super accessor + final val PARAMACCESSOR = 0x20000000; // for value definitions: is an access method for a final val parameter + // for parameters: is a val parameter + + final val CAPTURED = 0x40000000; // variable is accessed from nested function. Set by LambdaLift + final val BYNAMEPARAM = 0x40000000; // parameter is by name + + final val LABEL = 0x80000000L; // method symbol is a label. Set by TailCall + final val INCONSTRUCTOR = 0x80000000L; // class symbol is defined in this/superclass constructor. + + final val IS_ERROR = 0x100000000L; // symbol is an error symbol + final val OVERLOADED = 0x200000000L; // symbol is overloaded + final val LIFTED = 0x400000000L; // class has been lifted out to package level + // local value has been lifted out to class level + //todo: make LIFTED = latePRIVATE? + + final val MIXEDIN = 0x800000000L; // member has been mixed in + + final val EXPANDEDNAME = 0x1000000000L; // name has been expanded with class suffix + final val IMPLCLASS = 0x2000000000L; // symbol is an implementation class + final val TRANS_FLAG = 0x4000000000L; // transient flag guaranteed to be reset after each phase. + + final val LOCKED = 0x8000000000L; // temporary flag to catch cyclic dependencies + + final val InitialFlags = 0x000000FFFFFFFFFFL; // flags that are enabled from phase 1. + final val LateFlags = 0x000FFF0000000000L; // flags that override flags in 0xFFF. + final val AntiFlags = 0x7FF0000000000000L; // flags that cancel flags in 0x7FF + final val LateShift = 40L; + final val AntiShift = 52L; + + // late flags (set by a transformer phase) + final val latePRIVATE = (PRIVATE: long) << LateShift; + final val lateDEFERRED = (DEFERRED: long) << LateShift; + final val lateINTERFACE = (INTERFACE: long) << LateShift; + final val lateMODULE = (MODULE: long) << LateShift; + final val lateFINAL = (FINAL: long) << LateShift; + final val lateMETHOD = (METHOD: long) << LateShift; + final val notPRIVATE = (PRIVATE: long) << AntiShift; + final val notPROTECTED = (PROTECTED: long) << AntiShift; + final val notABSTRACT = (ABSTRACT: long) << AntiShift; + final val notOVERRIDE = (OVERRIDE: long) << AntiShift; + final val notMETHOD = (METHOD: long) << AntiShift; + + final val STATICMODULE = lateMODULE; + final val STATICMEMBER = notOVERRIDE; + + + // masks + /** This flags can be set when class or module symbol is first created. */ + final val TopLevelCreationFlags = + MODULE | PACKAGE | FINAL | JAVA; + + final val ExplicitFlags = // these modifiers can be set explicitly in source programs. + PRIVATE | PROTECTED | ABSTRACT | FINAL | SEALED | OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE; + + final val PrintableFlags = // these modifiers appear in TreePrinter output. + (ExplicitFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR | ACCESSOR | + SUPERACCESSOR | PARAMACCESSOR | BRIDGE | STATIC); + + final val FieldFlags = MUTABLE | CASEACCESSOR | PARAMACCESSOR | STATIC | FINAL; + + final val AccessFlags = PRIVATE | PROTECTED; + final val VARIANCES = COVARIANT | CONTRAVARIANT; + final val ConstrFlags = JAVA; + final val PickledFlags = 0xFFFFFFFF; + + /** Module flags inherited by their module-class */ + final val ModuleToClassFlags = AccessFlags | PACKAGE | CASE; + + def flagsToString(flags: long, suffixes: List[Pair[long, String]]): String = + (for (val i <- List.range(0, 63)) yield { + var s = flagToString(flags & (1L << i)); + suffixes.find(._1.==(i)) match { + case Some(Pair(i, suffix)) => s = s + "[" + suffix + "]" + case None => + } + s + }).filter("" !=).mkString("", " ", ""); + + def flagsToString(flags: long): String = flagsToString(flags, List()); + + private def flagToString(flag: long): String = { + if (flag == LABEL) "<label>" + else if (flag == INTERFACE) "<interface>" + else if (flag == IS_ERROR) "<is-error>" + else if (flag == OVERLOADED) "<overloaded>" + else if (flag == LIFTED) "<lifted>" + else if (flag == TRANS_FLAG) "<trans-flag>" + else if (flag == MIXEDIN) "<mixedin>" + else if (flag == EXPANDEDNAME) "<expandedname>" + else if (flag == LOCKED) "<locked>" + else if (flag == STATICMODULE) "<staticobject>" + else if (flag == STATICMEMBER) "<staticmember>" + else flag.asInstanceOf[int] match { + case IMPLICIT => "implicit" + case FINAL => "final" + case PRIVATE => "private" + case PROTECTED => "protected" + + case SEALED => "sealed" + case OVERRIDE => "override" + case CASE => "case" + case ABSTRACT => "abstract" + + case DEFERRED => "<deferred>" + case METHOD => "<method>" + case TRAIT => "<trait>" + case MODULE => "<module>" + + case MUTABLE => "<mutable>" + case PARAM => "<param>" + case PACKAGE => "<package>" + case DEPRECATED => "<deprecated>" + + case COVARIANT => "<covariant>" + case CONTRAVARIANT => "<contravariant>" + case ABSOVERRIDE => "abstract override" + case LOCAL => "<local>" + + case JAVA => "<java>" + case SYNTHETIC => "<synthetic>" + case STABLE => "<stable>" + case STATIC => "<static>" + + case CASEACCESSOR => "<caseaccessor>" + case ACCESSOR => "<accessor>" + + case SUPERACCESSOR => "<superaccessor>" + case PARAMACCESSOR => "<paramaccessor>" + case BRIDGE => "<bridge>" + case CAPTURED => "<captured>" + + case _ => "" + } + } + class Flag(mods : int) { + def isPrivate = ((mods & PRIVATE ) != 0); + def isProtected = ((mods & PROTECTED) != 0); + def isVariable = ((mods & MUTABLE) != 0); + def isPublic = !isPrivate && !isProtected; + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/InfoTransformers.scala b/src/compiler/scala/tools/nsc/symtab/InfoTransformers.scala new file mode 100644 index 0000000000..e1ff9bbac5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/InfoTransformers.scala @@ -0,0 +1,45 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +[_trait_] abstract class InfoTransformers: SymbolTable { + + abstract class InfoTransformer { + var prev: InfoTransformer = this; + var next: InfoTransformer = this; + + val pid: Phase#Id; + val changesBaseClasses: boolean; + def transform(sym: Symbol, tpe: Type): Type; + + def insert(that: InfoTransformer): unit = { + assert(this.pid != that.pid); + if (that.pid < this.pid) { + prev insert that + } else if (next.pid <= that.pid && next.pid != NoPhase.id) { + next insert that + } else { + that.next = next; + that.prev = this; + next.prev = that; + this.next = that + } + } + + def nextFrom(from: Phase#Id): InfoTransformer = + if (from == this.pid) this + else if (from < this.pid) + if (prev.pid < from) this + else prev.nextFrom(from); + else if (next.pid == NoPhase.id) next + else next.nextFrom(from); + } +} + + + + + diff --git a/src/compiler/scala/tools/nsc/symtab/Names.scala b/src/compiler/scala/tools/nsc/symtab/Names.scala new file mode 100644 index 0000000000..5fedb67ce4 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/Names.scala @@ -0,0 +1,332 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +import scala.tools.util.UTF8Codec; +import scala.tools.nsc.util.NameTransformer; + +class Names { + +// Operations ------------------------------------------------------------- + + private val HASH_SIZE = 0x8000; + private val HASH_MASK = 0x7FFF; + private val NAME_SIZE = 0x20000; + + final val nameDebug = false; + + /** memory to store all names sequentially + */ + var chrs: Array[char] = new Array[char](NAME_SIZE); + private var nc = 0; + + /** hashtable for finding term names quickly + */ + private val termHashtable = new Array[Name](HASH_SIZE); + + /** hashtable for finding type names quickly + */ + private val typeHashtable = new Array[Name](HASH_SIZE); + + /** the hashcode of a name + */ + 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 = i + 1; + i == len + } + + /** enter characters into chrs array + */ + private def enterChars(cs: Array[char], offset: int, len: int): unit = { + var i = 0; + while (i < len) { + if (nc + i == chrs.length) { + val newchrs = new Array[char](chrs.length * 2); + System.arraycopy(chrs, 0, newchrs, 0, chrs.length); + chrs = newchrs; + } + chrs(nc + i) = cs(offset + i); + i = i + 1 + } + if (len == 0) nc = nc + 1 + else nc = nc + len + } + + /** create a term name from the characters in cs[offset..offset+len-1]. + */ + def newTermName(cs: Array[char], offset: int, len: int): Name = { + val h = hashValue(cs, offset, len) & HASH_MASK; + var n = termHashtable(h); + while ((n != null) && (n.length != len || !equals(n.start, cs, offset, len))) { + n = n.next; + } + if (n == null) { + n = new TermName(nc, len, h); + enterChars(cs, offset, len); + } + n + } + + /** create a term name from string + */ + def newTermName(s: String): Name = + newTermName(s.toCharArray(), 0, s.length()); + + /** create a term name from the UTF8 encoded bytes in bs[offset..offset+len-1]. + */ + def newTermName(bs: Array[byte], offset: int, len: int): Name = { + val cs = new Array[char](bs.length); + val nchrs = UTF8Codec.decode(bs, offset, cs, 0, len); + newTermName(cs, 0, nchrs) + } + + /** create a type name from the characters in cs[offset..offset+len-1]. + */ + def newTypeName(cs: Array[char], offset: int, len: int): Name = + newTermName(cs, offset, len).toTypeName; + + /** create a type name from string + */ + def newTypeName(s: String): Name = + newTermName(s).toTypeName; + + /** create a type name from the UTF8 encoded bytes in bs[offset..offset+len-1]. + */ + def newTypeName(bs: Array[byte], offset: int, len: int): Name = + newTermName(bs, offset, len).toTypeName; + + + def nameChars: Array[char] = chrs; + + implicit def view(s: String): Name = newTermName(s); + +// Classes ---------------------------------------------------------------------- + + /** The name class */ + abstract class Name(index: int, len: int) extends Function1[int, char] { + + /** Index into name table */ + def start: int = index; + + /** next name in the same hash bucket + */ + var next: Name = null; + + /** return the length of this name + */ + final def length: int = len; + + final def isEmpty = length == 0; + + def isTermName: boolean; + def isTypeName: boolean; + def toTermName: Name; + def toTypeName: Name; + + + /** copy bytes of this name to buffer cs, starting at offset + */ + final def copyChars(cs: Array[char], offset: int) = + System.arraycopy(chrs, index, cs, offset, len); + + /** return the ascii representation of this name + */ + final def toChars = { + val cs = new Array[char](len); + copyChars(cs, 0); + cs + } + + /** return the string representation of this name + */ + final override def toString(): String = new String(chrs, index, len); + + /** 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 = + UTF8Codec.encode(chrs, index, bs, offset, len); + + /** return the hash value of this name + */ + final override def hashCode(): int = index; + + /** return the i'th char of this name + */ + final def apply(i: int): char = chrs(index + i); + + /** return the index of first occurrence of char c in this name, length if not found */ + final def pos(c: char): int = pos(c, 0); + + /** return the index of first occurrence of char c in this name, length if not found */ + final def pos(s: String): int = pos(s, 0); + + /** return the index of first occurrence of char c in this name from `start', + * length if not found */ + final def pos(c: char, start: int): int = { + var i = start; + while (i < len && chrs(index + i) != c) i = i + 1; + i + } + + /** return the index of first occurrence of nonempty string s in this name from `start', + * length if not found */ + final def pos(s: String, start: int): int = { + var i = pos(s.charAt(0), start); + while (i + s.length() <= len) { + var j = 1; + while (s.charAt(j) == chrs(index + i + j)) { + j = j + 1; + if (j == s.length()) return i; + } + i = pos(s.charAt(0), i + 1) + } + len + } + + /** return the index of last occurrence of char c in this name, -1 if not found. + */ + final def lastPos(c: char): int = lastPos(c, len - 1); + + final def lastPos(s: String): int = lastPos(s, len - s.length()); + + /** return the index of last occurrence of char c in this name from `start', + * -1 if not found + */ + final def lastPos(c: char, start: int): int = { + var i = start; + while (i >= 0 && chrs(index + i) != c) i = i - 1; + i + } + + /** return the index of last occurrence of string s in this name from `start', + * -1 if not found + */ + final def lastPos(s: String, start: int): int = { + var i = lastPos(s.charAt(0), start); + while (i >= 0) { + var j = 1; + while (s.charAt(j) == chrs(index + i + j)) { + j = j + 1; + if (j == s.length()) return i; + } + i = lastPos(s.charAt(0), i - 1) + } + -s.length() + } + + /** does this name start with prefix? + */ + final def startsWith(prefix: Name): boolean = startsWith(prefix, 0); + + /** does this name start with prefix at given start index? + */ + final def startsWith(prefix: Name, start: int): boolean = { + var i = 0; + while (i < prefix.length && start + i < len && chrs(index + start + i) == chrs(prefix.start + i)) + i = i + 1; + i == prefix.length + } + + /** does this name end with suffix? + */ + final def endsWith(suffix: Name): boolean = endsWith(suffix, len); + + /** does this name end with suffix just before given end index? + */ + final def endsWith(suffix: Name, end: int): boolean = { + var i = 1; + while (i <= suffix.length && i <= end && chrs(index + end - i) == chrs(suffix.start + suffix.length - i)) + i = i + 1; + i > suffix.length + } + + /** the subname with characters from start to end-1 + */ + def subName(from: int, to: int): Name; + + /** replace all occurrences of `from' by `to' in name. + * result is always a term name. + */ + def replace(from: char, to: char): Name = { + val cs = new Array[char](len); + var i = 0; + while (i < len) { + val ch = this(i); + cs(i) = if (ch == from) to else ch; + i = i + 1 + } + newTermName(cs, 0, len) + } + + /** Replace operator symbols by corresponding "$op_name" */ + def encode: Name = { + val str = toString(); + val res = NameTransformer.encode(str); + if (res == str) this + else if (isTypeName) newTypeName(res) + else newTermName(res) + } + + /** Replace $op_name by corresponding operator symbol */ + def decode: String = ( + NameTransformer.decode(toString()) + + (if (nameDebug && isTypeName) "!" else ""));//debug + } + + private class TermName(index: int, len: int, hash: int) extends Name(index, len) { + next = termHashtable(hash); + termHashtable(hash) = this; + def isTermName: boolean = true; + def isTypeName: boolean = false; + def toTermName: Name = this; + def toTypeName = { + val h = hashValue(chrs, index, len) & HASH_MASK; + var n = typeHashtable(h); + while (n != null && n.start != index) + n = n.next; + if (n == null) + n = new TypeName(index, len, h); + n + } + def subName(from: int, to: int): Name = + newTermName(chrs, start + from, to - from); + } + + private class TypeName(index: int, len: int, hash: int) extends Name(index, len) { + next = typeHashtable(hash); + typeHashtable(hash) = this; + def isTermName: boolean = false; + def isTypeName: boolean = true; + def toTermName: Name = { + val h = hashValue(chrs, index, len) & HASH_MASK; + var n = termHashtable(h); + while (n != null && n.start != index) + n = n.next; + if (n == null) + n = new TermName(index, len, h); + n + } + def toTypeName: Name = this; + def subName(from: int, to: int): Name = + newTypeName(chrs, start + from, to - from); + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/Scopes.scala b/src/compiler/scala/tools/nsc/symtab/Scopes.scala new file mode 100644 index 0000000000..fe8ffaa47b --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/Scopes.scala @@ -0,0 +1,249 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +[_trait_] abstract class Scopes: SymbolTable { + + class ScopeEntry(val sym: Symbol, val owner: Scope) { + + /** the next entry in the hash bucket + */ + var tail: ScopeEntry = _; + + /** the next entry in this scope + */ + var next: ScopeEntry = null; + + override def hashCode(): int = sym.name.start; + override def toString(): String = sym.toString(); + } + + def newScopeEntry(sym: Symbol, owner: Scope): ScopeEntry = { + val e = new ScopeEntry(sym, owner); + e.next = owner.elems; + owner.elems = e; + e + } + + object NoScopeEntry extends ScopeEntry(NoSymbol, null); + + class Scope(initElems: ScopeEntry) { + + var elems: ScopeEntry = initElems; + + /** The number of times this scope is neted in another + */ + private var nestinglevel = 0; + + /** 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; + + /** size and mask of hash tables + * todo: make hashtables grow? + */ + private val HASHSIZE = 0x80; + private val HASHMASK = 0x7f; + + /** the threshold number of entries from which a hashtable is constructed. + */ + private val MIN_HASH = 8; + + if (size >= MIN_HASH) createHash; + + def this() = this(null: ScopeEntry); + + def this(base: Scope) = { + this(base.elems); +/* + if (base.hashtable != null) { + this.hashtable = new Array[ScopeEntry](HASHSIZE); + System.arraycopy(base.hashtable, 0, this.hashtable, 0, HASHSIZE); + } +*/ + nestinglevel = base.nestinglevel + 1 + } + + def this(decls: List[Symbol]) = { + this(); + decls foreach enter + } + + /** Returns a new scope with the same content as this one. */ + def cloneScope: Scope = { + val clone = new Scope(); + this.toList foreach clone.enter; + clone + } + + /** is the scope empty? */ + def isEmpty: boolean = elems == null; + + /** the number of entries in this scope */ + def size: int = { + var s = 0; + var e = elems; + while (e != null) { + s = s + 1; + e = e.next + } + s + } + + /** enter a scope entry + */ + def enter(e: ScopeEntry): unit = { + elemsCache = null; + if (hashtable != null) { + val i = e.sym.name.start & HASHMASK; + elems.tail = hashtable(i); + hashtable(i) = elems; + } else if (size >= MIN_HASH) { + createHash; + } + } + + /** enter a symbol + */ + def enter(sym: Symbol): unit = enter(newScopeEntry(sym, this)); + + /** enter a symbol, asserting that no symbol with same name exists in scope + */ + def enterUnique(sym: Symbol): unit = { + assert(lookup(sym.name) == NoSymbol); + enter(sym); + } + + private def createHash: unit = { + hashtable = new Array[ScopeEntry](HASHSIZE); + enterInHash(elems); + } + + private def enterInHash(e: ScopeEntry): unit = { + if (e != null) { + enterInHash(e.next); + val i = e.sym.name.start & HASHMASK; + e.tail = hashtable(i); + hashtable(i) = e; + } + } + + /** remove entry + */ + def unlink(e: ScopeEntry): unit = { + if (elems == e) { + elems = e.next; + } else { + var e1 = elems; + while (e1.next != e) e1 = e1.next; + e1.next = e.next; + } + if (hashtable != null) { + var e1 = hashtable(e.sym.name.start & HASHMASK); + if (e1 == e) { + hashtable(e.sym.name.start & HASHMASK) = e.tail; + } else { + while (e1.tail != e) e1 = e1.tail; + e1.tail = e.tail; + } + } + elemsCache = null + } + + /** remove symbol */ + def unlink(sym: Symbol): unit = { + var e = lookupEntry(sym.name); + while (e != null) { + if (e.sym == sym) unlink(e); + e = lookupNextEntry(e) + } + } + + /** lookup a symbol + */ + def lookup(name: Name): Symbol = { + val e = lookupEntry(name); + if (e == null) NoSymbol else e.sym; + } + + /** lookup a symbol entry matching given name + */ + def lookupEntry(name: Name): ScopeEntry = { + var e: ScopeEntry = null; + if (false & hashtable != null) { + e = hashtable(name.start & HASHMASK); + while (e != null && e.sym.name != name) e = e.tail; + } else { + e = elems; + while (e != null && e.sym.name != name) e = e.next; + } + e + } + + /** lookup next entry with same name as this one */ + def lookupNextEntry(entry: ScopeEntry): ScopeEntry = { + var e = entry; + if (hashtable != null) //debug + do { e = e.tail } while (e != null && e.sym.name != entry.sym.name) + else + do { e = e.next } while (e != null && e.sym.name != entry.sym.name); + e + } + + /** Return all symbols as a list in the order they were entered in this scope. + */ + def toList: List[Symbol] = { + if (elemsCache == null) { + elemsCache = Nil; + var e = elems; + while (e != null && e.owner == this) { + elemsCache = e.sym :: elemsCache; + e = e.next + } + } + elemsCache + } + + /** Return all symbols as an interator in the order they were entered in this scope. + */ + def elements: Iterator[Symbol] = toList.elements; + + def mkString(start: String, sep: String, end: String) = + toList.map(.defString).mkString(start, sep, end); + + override def toString(): String = mkString("{\n ", ";\n ", "\n}"); + + /** Return the nesting level of this scope, i.e. the number of times this scope + * was nested in another */ + def nestingLevel = nestinglevel; + } + + /** The empty scope (immutable). + */ + object EmptyScope extends Scope { + override def enter(e: ScopeEntry): unit = + throw new Error("EmptyScope.enter"); + } + + /** The error scope. + */ + class ErrorScope(owner: Symbol) extends Scope(null: ScopeEntry) { + override def lookupEntry(name: Name): ScopeEntry = { + val e = super.lookupEntry(name); + if (e != NoSymbol) e + else { + enter(if (name.isTermName) owner.newErrorValue(name) + else owner.newErrorClass(name)); + super.lookupEntry(name); + } + } + } +} + diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala new file mode 100644 index 0000000000..2b8b8692f5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -0,0 +1,356 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +import scala.tools.nsc.util.NameTransformer; + +[_trait_] abstract class StdNames: SymbolTable { + + object nme { + + // Scala keywords; enter them first to minimize scanner.maxKey + val ABSTRACTkw = newTermName("abstract"); + val CASEkw = newTermName("case"); + val CLASSkw = newTermName("class"); + val CATCHkw = newTermName("catch"); + val DEFkw = newTermName("def"); + val DOkw = newTermName("do"); + val ELSEkw = newTermName("else"); + val EXTENDSkw = newTermName("extends"); + val FALSEkw = newTermName("false"); + val FINALkw = newTermName("final"); + val FINALLYkw = newTermName("finally"); + val FORkw = newTermName("for"); + val IFkw = newTermName("if"); + val IMPLICITkw = newTermName("implicit"); + val IMPORTkw = newTermName("import"); + val MATCHkw = newTermName("match"); + val NEWkw = newTermName("new"); + val NULLkw = newTermName("null"); + val OBJECTkw = newTermName("object"); + val OUTER = newTermName("$outer"); + val OVERRIDEkw = newTermName("override"); + val PACKAGEkw = newTermName("package"); + val PRIVATEkw = newTermName("private"); + val PROTECTEDkw = newTermName("protected"); + val RETURNkw = newTermName("return"); + val REQUIRESkw = newTermName("requires"); + val SEALEDkw = newTermName("sealed"); + val SUPERkw = newTermName("super"); + val THISkw = newTermName("this"); + val THROWkw = newTermName("throw"); + val TRAITkw = newTermName("trait"); + val TRUEkw = newTermName("true"); + val TRYkw = newTermName("try"); + val TYPEkw = newTermName("type"); + val VALkw = newTermName("val"); + val VARkw = newTermName("var"); + val WITHkw = newTermName("with"); + val WHILEkw = newTermName("while"); + val YIELDkw = newTermName("yield"); + val DOTkw = newTermName("."); + val USCOREkw = newTermName("_"); + val COLONkw = newTermName(":"); + val EQUALSkw = newTermName("="); + val ARROWkw = newTermName("=>"); + val LARROWkw = newTermName("<-"); + val SUBTYPEkw = newTermName("<:"); + val VIEWBOUNDkw = newTermName("<%"); + val SUPERTYPEkw = newTermName(">:"); + val HASHkw = newTermName("#"); + val ATkw = newTermName("@"); + + val LOCALDUMMY_PREFIX_STRING = "local$"; + val SUPER_PREFIX_STRING = "super$"; + val EXPAND_SEPARATOR_STRING = "$$"; + val TUPLE_FIELD_PREFIX_STRING = "_"; + + def LOCAL(clazz: Symbol) = newTermName(LOCALDUMMY_PREFIX_STRING + clazz.name); + def TUPLE_FIELD(index: int) = newTermName(TUPLE_FIELD_PREFIX_STRING + index); + + val LOCAL_SUFFIX = newTermName(" "); + val SETTER_SUFFIX = encode("_="); + val IMPL_CLASS_SUFFIX = newTermName("$class"); + val MODULE_SUFFIX = newTermName("$module"); + val LOCALDUMMY_PREFIX = newTermName(LOCALDUMMY_PREFIX_STRING); + val THIS_SUFFIX = newTermName(".this"); + + def isLocalName(name: Name) = name.endsWith(LOCAL_SUFFIX); + def isSetterName(name: Name) = name.endsWith(SETTER_SUFFIX); + def isLocalDummyName(name: Name) = name.startsWith(LOCALDUMMY_PREFIX); + +// def originalName(name: Name): Name = { + def originalName(name: Name): Name = { + var i = name.length; + while (i >= 2 && !(name(i - 1) == '$' && name(i - 2) == '$')) i = i - 1; + if (i >= 2) { + while (i >= 3 && name(i - 3) == '$') i = i - 1; + name.subName(i, name.length) + } else name + } +// val result = originalName(name); +// System.out.println("oroginal " + name + " = " + result); +// result +// } + + def localToGetter(name: Name): Name = { + assert(isLocalName(name));//debug + name.subName(0, name.length - LOCAL_SUFFIX.length); + } + + def getterToLocal(name: Name): Name = { + assert(!isLocalName(name) && !isSetterName(name));//debug + newTermName(name.toString() + LOCAL_SUFFIX); + } + + def getterToSetter(name: Name): Name = { + assert(!isLocalName(name) && !isSetterName(name));//debug + newTermName(name.toString() + SETTER_SUFFIX); + } + + def setterToGetter(name: Name): Name = { + name.subName(0, name.length - SETTER_SUFFIX.length); + } + + def getterName(name: Name): Name = + if (isLocalName(name)) localToGetter(name) else name; + + def isImplClassName(name: Name): boolean = + name endsWith IMPL_CLASS_SUFFIX; + + def implClassName(name: Name): Name = + newTypeName(name.toString() + IMPL_CLASS_SUFFIX); + + def moduleVarName(name: Name): Name = + newTermName(name.toString() + MODULE_SUFFIX); + + def isModuleVarName(name: Name): boolean = + name.endsWith(MODULE_SUFFIX); + + def superName(name: Name) = newTermName("super$" + name); + + val ERROR = newTermName("<error>"); + val ERRORtype = newTypeName("<error>"); + + val NOSYMBOL = newTermName("<none>"); + val EMPTY = newTermName(""); + val ANYNAME = newTermName("<anyname>"); + val WILDCARD = newTermName("_"); + val WILDCARD_STAR = newTermName("_*"); + val COMPOUND_NAME = newTermName("<ct>"); + val ANON_CLASS_NAME = newTermName("$anon"); + val ANON_FUN_NAME = newTermName("$anonfun"); + val REFINE_CLASS_NAME = newTermName("<refinement>"); + val EMPTY_PACKAGE_NAME = newTermName("<empty>"); + val IMPORT = newTermName("<import>"); + val ZERO = newTermName("<zero>"); + val STAR = newTermName("*"); + val ROOT = newTermName("<root>"); + val REPEATED_PARAM_CLASS_NAME = newTermName("<repeated>"); + val BYNAME_PARAM_CLASS_NAME = newTermName("<byname>"); + val SELF = newTermName("$this"); + + val CONSTRUCTOR = newTermName("<init>"); + val MIXIN_CONSTRUCTOR = newTermName("$init$"); + val INITIALIZER = newTermName("<init>"); + val INLINED_INITIALIZER = newTermName("$init$"); + + val MINUS = encode("-"); + val PLUS = encode("+"); + val TILDE = encode("~"); + val EQEQ = encode("=="); + val BANG = encode("!"); + val BANGEQ = encode("!="); + val BARBAR = encode("||"); + val AMPAMP = encode("&&"); + val COLONCOLON = encode("::"); + val PERCENT = encode("%"); + + val All = newTermName("All"); + val AllRef = newTermName("AllRef"); + val Any = newTermName("Any"); + val AnyVal = newTermName("AnyVal"); + val AnyRef = newTermName("AnyRef"); + val Array = newTermName("Array"); + val Byte = newTermName("Byte"); + val CaseClass = newTermName("CaseClass"); + val Catch = newTermName("Catch"); + val Char = newTermName("Char"); + val Boolean = newTermName("Boolean"); + val Do = newTermName("Do"); + val Double = newTermName("Double"); + val Element = newTermName("Element"); + val Finally = newTermName("Finally"); + val Float = newTermName("Float"); + val Function = newTermName("Function"); + val Int = newTermName("Int"); + val Labelled = newTermName("Labelled"); + val List = newTermName("List"); + val Long = newTermName("Long"); + val Nil = newTermName("Nil"); + val Object = newTermName("Object"); + val PartialFunction = newTermName("PartialFunction"); + val Predef = newTermName("Predef"); + val ScalaObject = newTermName("ScalaObject"); + val ScalaRunTime = newTermName("ScalaRunTime"); + val Seq = newTermName("Seq"); + val Short = newTermName("Short"); + val SourceFile = newTermName("SourceFile"); + val String = newTermName("String"); + val Symbol = newTermName("Symbol"); + val Synthetic = newTermName("Synthetic"); + + val Text = newTermName("Text"); + val Throwable = newTermName("Throwable"); + val Try = newTermName("Try"); + val Tuple = newTermName("Tuple"); + val Type = newTermName("Type"); + val Tuple2 = newTermName("Tuple2"); + val Unit = newTermName("Unit"); + val While = newTermName("While"); + val apply = newTermName("apply"); + val array = newTermName("array"); + val assert_ = newTermName("assert"); + val assume_ = newTermName("assume"); + val asInstanceOf = newTermName("asInstanceOf"); + val asInstanceOfErased = newTermName("asInstanceOf$erased"); + val box = newTermName("box"); + val caseArity = newTermName("caseArity"); + val caseElement = newTermName("caseElement"); + val caseName = newTermName("caseName"); + val checkCastability = newTermName("checkCastability"); + val coerce = newTermName("coerce"); + val defaultValue = newTermName("defaultValue"); + val dummy = newTermName("$dummy"); + val elem = newTermName("elem"); + val elements = newTermName("elements"); + val eq = newTermName("eq"); + val equals_ = newTermName("equals"); + val fail = newTermName("fail"); + val report = newTermName("report"); + val false_ = newTermName("false"); + val filter = newTermName("filter"); + val finalize_ = newTermName("finalize"); + val flatMap = newTermName("flatMap"); + val foreach = newTermName("foreach"); + val getClass_ = newTermName("getClass"); + val hasAsInstance = newTermName("hasAsInstance"); + val hashCode_ = newTermName("hashCode"); + val hasNext = newTermName("hasNext"); + val head = newTermName("head"); + val isInstanceOf = newTermName("isInstanceOf"); + val isInstanceOfErased = newTermName("isInstanceOf$erased"); + val isDefinedAt = newTermName("isDefinedAt"); + val isEmpty = newTermName("isEmpty"); + val java = newTermName("java"); + val lang = newTermName("lang"); + val length = newTermName("length"); + val map = newTermName("map"); + val n = newTermName("n"); + val nobinding = newTermName("nobinding"); + val next = newTermName("next"); + val newArray = newTermName("newArray"); + val notify_ = newTermName("notify"); + val notifyAll_ = newTermName("notifyAll"); + val null_ = newTermName("null"); + val predef = newTermName("predef"); + val print = newTermName("print"); + val runtime = newTermName("runtime"); + val readResolve = newTermName("readResolve"); + val scala_ = newTermName("scala"); + val xml = newTermName("xml"); + val synchronized_ = newTermName("synchronized"); + val tail = newTermName("tail"); + val toString_ = newTermName("toString"); + val that = newTermName("that"); + val that1 = newTermName("that1"); + val this_ = newTermName("this"); + val throw_ = newTermName("throw"); + val true_ = newTermName("true"); + val update = newTermName("update"); + val view_ = newTermName("view"); + val tag = newTermName("$tag"); + val wait_ = newTermName("wait"); + + val ZNOT = encode("!"); + val ZAND = encode("&&"); + val ZOR = encode("||"); + val NOT = encode("~"); + val ADD = encode("+"); + val SUB = encode("-"); + val MUL = encode("*"); + val DIV = encode("/"); + val MOD = encode("%"); + val EQ = encode("=="); + val NE = encode("!="); + val LT = encode("<"); + val LE = encode("<="); + val GT = encode(">"); + val GE = encode(">="); + val OR = encode("|"); + val XOR = encode("^"); + val AND = encode("&"); + val LSL = encode("<<"); + val LSR = encode(">>>"); + val ASR = encode(">>"); + + // value-conversion methods + val toByte = newTermName("toByte"); + val toShort = newTermName("toShort"); + val toChar = newTermName("toChar"); + val toInt = newTermName("toInt"); + val toLong = newTermName("toLong"); + val toFloat = newTermName("toFloat"); + val toDouble = newTermName("toDouble"); + + val SourceFileATTR = newTermName("SourceFile"); + val SyntheticATTR = newTermName("Synthetic"); + val BridgeATTR = newTermName("Bridge"); + val DeprecatedATTR = newTermName("Deprecated"); + val CodeATTR = newTermName("Code"); + val ExceptionsATTR = newTermName("Exceptions"); + val ConstantValueATTR = newTermName("ConstantValue"); + val LineNumberTableATTR = newTermName("LineNumberTable"); + val LocalVariableTableATTR = newTermName("LocalVariableTable"); + val InnerClassesATTR = newTermName("InnerClasses"); + val JacoMetaATTR = newTermName("JacoMeta"); + val ScalaSignatureATTR = newTermName("ScalaSig"); + val JavaInterfaceATTR = newTermName("JacoInterface"); + + // '_' is temporary + val _Attribute = newTypeName("Attribute"); + val _MetaData = newTypeName("MetaData"); + val _NamespaceBinding = newTypeName("NamespaceBinding"); + val _NodeBuffer = newTypeName("NodeBuffer"); + val _Null = newTermName("Null"); + + val _PrefixedAttribute = newTypeName("PrefixedAttribute"); + val _UnprefixedAttribute = newTypeName("UnprefixedAttribute"); + val _Elem = newTypeName("Elem"); + val _Seq = newTypeName("Seq"); + val _immutable = newTermName("immutable"); + val _mutable = newTermName("mutable"); + val _append = newTermName("append"); + val _plus = newTermName("$amp$plus"); + val _collection = newTermName("collection"); + val _toList = newTermName("toList"); + val _xml = newTermName("xml"); + val _Comment = newTypeName("Comment"); + val _CharData = newTypeName("CharData"); + val _Node = newTypeName("Node"); + val _None = newTermName("None"); + val _Some = newTypeName("Some"); + val _ProcInstr = newTypeName("ProcInstr"); + val _Text = newTypeName("Text"); + val _EntityRef = newTypeName("EntityRef"); + final val _md = newTermName("$md"); + final val _scope = newTermName("$scope"); + final val _tmpscope = newTermName("$tmpscope"); + + } + + def encode(str: String): Name = newTermName(NameTransformer.encode(str)); +} diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala new file mode 100644 index 0000000000..11f0c21c4f --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -0,0 +1,192 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +import java.io.IOException; +import scala.tools.nsc.util.Position; +import scala.tools.util.{AbstractFile}; +import scala.tools.nsc.util.NameTransformer; +import scala.collection.mutable.HashMap; +import classfile.{ClassfileParser, SymblfileParser}; +import Flags._; + + +abstract class SymbolLoaders { + val global: Global; + import global._; + + /** A lazy type that completes itself by calling parameter doComplete. + * Any linked modules/classes or module classes are also initialized. + * @param doComplete The type completion procedure to be run. + * It takes symbol to compkete as parameter and returns + * name of file loaded for completion as a result. + * Can throw an IOException on error. + */ + abstract class SymbolLoader(file: AbstractFile) extends LazyType { + /** Load source or class file for `root', return */ + protected def doComplete(root: Symbol): unit; + /** The kind of file that's processed by this loader */ + protected def kindString: String; + private var ok = false; + private def setSource(sym: Symbol): unit = sym match { + case clazz: ClassSymbol => clazz.sourceFile = file; + case _ => + } + override def complete(root: Symbol): unit = { + try { + val start = System.currentTimeMillis(); + val currentphase = phase; + doComplete(root); + phase = currentphase; + def source = kindString + " " + file; + informTime("loaded " + source, start); + if (root.rawInfo != this) { + ok = true; + setSource(root.linkedModule); + setSource(root.linkedClass); + } else error(source + " does not define " + root) + } catch { + case ex: IOException => + if (settings.debug.value) ex.printStackTrace(); + val msg = ex.getMessage(); + error( + if (msg == null) "i/o error while loading " + root.name + else "error while loading " + root.name + ", " + msg); + } + initRoot(root); + if (!root.isPackageClass) initRoot(root.linkedSym); + } + override def load(root: Symbol): unit = complete(root); + + private def initRoot(root: Symbol): unit = { + if (root.rawInfo == this) { + root.setInfo(if (ok) NoType else ErrorType); + if (root.isModule) + root.moduleClass.setInfo(if (ok) NoType else ErrorType) + } + if (root.isClass && !root.isModuleClass) root.rawInfo.load(root) + } + } + + /** Load contents of a package + */ + class PackageLoader(directory: AbstractFile) extends SymbolLoader(directory) { + protected def doComplete(root: Symbol): unit = { + assert(root.isPackageClass, root); + root.setInfo(new PackageClassInfoType(new Scope(), root)); + + /** Is the given name a valid input file base name? */ + def isValid(name: String): boolean = + name.length() > 0 && !name.endsWith("$class") && name.indexOf("$anon") == -1; + + def enterPackage(str: String, completer: SymbolLoader): unit = { + val pkg = root.newPackage(Position.NOPOS, newTermName(str)); + pkg.moduleClass.setInfo(completer); + pkg.setInfo(pkg.moduleClass.tpe); + root.info.decls.enter(pkg) + } + + def enterClassAndModule(str: String, completer: SymbolLoader, sfile : AbstractFile): unit = { + val owner = if (root.isRoot) definitions.EmptyPackageClass else root; + val name = newTermName(str); + val clazz = owner.newClass(Position.NOPOS, name.toTypeName); + val module = owner.newModule(Position.NOPOS, name); + clazz.sourceFile = sfile; + clazz.setInfo(completer); + module.setInfo(completer); + module.moduleClass.setInfo(moduleClassLoader); + owner.info.decls.enter(clazz); + owner.info.decls.enter(module); + assert(clazz.linkedModule == module, module); + assert(module.linkedClass == clazz, clazz); + } + + val sources = new HashMap[String, AbstractFile]; + val classes = new HashMap[String, AbstractFile]; + val packages = new HashMap[String, AbstractFile]; + val it = directory.list(); + while (it.hasNext()) { + val file = it.next().asInstanceOf[AbstractFile]; + val filename = file.getName(); + if (file.isDirectory()) { + if (filename != "META_INF" && !packages.isDefinedAt(filename)) packages(filename) = file; +/* + } else if (filename.endsWith(".symbl")) { + val name = filename.substring(0, filename.length() - 6); + if (isValid(name) && + (!classes.isDefinedAt(name) || classes(name).getName().endsWith(".class"))) + classes(name) = file; +*/ + } else if (filename.endsWith(".class")) { + val name = filename.substring(0, filename.length() - 6); + if (isValid(name) && !classes.isDefinedAt(name)) + classes(name) = file; + } else if (filename.endsWith(".scala")) { + val name = filename.substring(0, filename.length() - 6); + if (isValid(name) && !sources.isDefinedAt(name)) + sources(name) = file; + } + } + for (val Pair(name, sfile) <- sources.elements) { + classes.get(name) match { + case Some(cfile) if (cfile.lastModified() >= sfile.lastModified()) => {} + case _ => enterClassAndModule(name, new SourcefileLoader(sfile), sfile); + } + } + for (val Pair(name, cfile) <- classes.elements) { + val sfile = sources.get(name) match { + case Some(sfile0) => sfile0; + case _ => null; + } + sources.get(name) match { + case Some(sfile) if (sfile.lastModified() > cfile.lastModified()) => {} + case _ => + val loader = +/* if (cfile.getName().endsWith(".symbl")) new SymblfileLoader(cfile) + else */ + new ClassfileLoader(cfile); + enterClassAndModule(name, loader, sfile) + } + } + for (val Pair(name, file) <- packages.elements) { + if (!sources.contains(name) && !classes.contains(name)) + enterPackage(name, new PackageLoader(file)); + } + } + protected def kindString: String = "directory path" + } + + private object classfileParser extends ClassfileParser { + val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global; + } + +/* + private object symblfileParser extends SymblfileParser { + val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global; + } +*/ + + class ClassfileLoader(file: AbstractFile) extends SymbolLoader(file) { + protected def doComplete(root: Symbol): unit = classfileParser.parse(file, root); + protected def kindString: String = "class file"; + } +/* + class SymblfileLoader(file: AbstractFile) extends SymbolLoader(file) { + protected def doComplete(root: Symbol): unit = symblfileParser.parse(file, root); + protected def kindString: String = "symbl file"; + } +*/ + class SourcefileLoader(file: AbstractFile) extends SymbolLoader(file) { + protected def doComplete(root: Symbol): unit = global.currentRun.compileLate(file); + protected def kindString: String = "source file"; + } + + object moduleClassLoader extends SymbolLoader(null) { + protected def doComplete(root: Symbol): unit = + root.sourceModule.initialize; + protected def kindString: String = ""; + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala new file mode 100644 index 0000000000..eb29dad289 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala @@ -0,0 +1,50 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +import util._; + +abstract class SymbolTable extends Names + with Symbols + with Types + with Scopes + with Definitions + with Constants + with InfoTransformers + with StdNames { + def settings: Settings; + def rootLoader: LazyType; + def log(msg: Object): unit; + + private var ph: Phase = NoPhase; + def phase: Phase = ph; + def phase_=(p: Phase): unit = { + //System.out.println("setting phase to " + p); + assert(p != null && p != NoPhase); + ph = p + } + + final val NoRun = null; + + /** The current compiler run. */ + def currentRun: CompilerRun; + + def atPhase[T](ph: Phase)(op: => T): T = { + val current = phase; + phase = ph; + val result = op; + phase = current; + result + } + + var infoTransformers = new InfoTransformer { + val pid = NoPhase.id; + val changesBaseClasses = true; + def transform(sym: Symbol, tpe: Type): Type = tpe; + } + + val phaseWithId: Array[Phase]; +} diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala new file mode 100644 index 0000000000..6bdf29c54e --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -0,0 +1,1055 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +import scala.tools.util.AbstractFile; +import scala.tools.nsc.util.{Position, SourceFile}; +import Flags._; + +[_trait_] abstract class Symbols: SymbolTable { + import definitions._; + + private var ids = 0; + + //for statistics: + def symbolCount = ids; + var typeSymbolCount = 0; + var classSymbolCount = 0; + + type AttrInfo = Pair[Type, List[Constant]]; + + val emptySymbolArray = new Array[Symbol](0); + + /** The class for all symbols */ + abstract class Symbol(initOwner: Symbol, initPos: int, initName: Name) { + + var rawowner = initOwner; + var rawname = initName; + private var rawflags: long = 0; + private var rawpos = initPos; + val id = { ids = ids + 1; ids } + + var validForRun: CompilerRun = NoRun; + + def pos = rawpos; + def setPos(pos: int): this.type = { this.rawpos = pos; this } + + def namePos(source : SourceFile) = { + val buf = source.content; + if (pos == Position.NOPOS) Position.NOPOS; + else if (isTypeParameter) pos - name.length; + else if (isVariable || isMethod || isClass || isModule) { + var ret = pos; + + if (buf(pos) == ',') ret = ret + 1; + else if (isClass) ret = ret + ("class").length(); + else if (isModule) ret = ret + ("object").length(); + else ret = ret + ("var").length(); + while (Character.isWhitespace(buf(ret))) ret = ret + 1; + ret; + } + else if (isValue) pos; + else -1; + } + + var attributes: List[AttrInfo] = List(); + + var privateWithin: Symbol = null; + +// Creators ------------------------------------------------------------------- + + final def newValue(pos: int, name: Name) = + new TermSymbol(this, pos, name); + final def newVariable(pos: int, name: Name) = + newValue(pos, name).setFlag(MUTABLE); + final def newValueParameter(pos: int, name: Name) = + newValue(pos, name).setFlag(PARAM); + final def newLocalDummy(pos: int) = + newValue(pos, nme.LOCAL(this)).setInfo(NoType); + final def newMethod(pos: int, name: Name) = + newValue(pos, name).setFlag(METHOD); + final def newLabel(pos: int, name: Name) = + newMethod(pos, name).setFlag(LABEL); + final def newConstructor(pos: int) = + newMethod(pos, nme.CONSTRUCTOR); + final def newModule(pos: int, name: Name, clazz: ClassSymbol) = + new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL).setModuleClass(clazz); + final def newModule(pos: int, name: Name) = { + val m = new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL); + m.setModuleClass(new ModuleClassSymbol(m)) + } + final def newPackage(pos: int, name: Name) = { + assert(isPackageClass); + val m = newModule(pos, name).setFlag(JAVA | PACKAGE); + m.moduleClass.setFlag(JAVA | PACKAGE); + m + } + final def newThisSym(pos: int) = { + newValue(pos, nme.this_).setFlag(SYNTHETIC); + } + final def newThisSkolem: Symbol = + new ThisSkolem(owner, pos, name, this) + .setFlag(SYNTHETIC | FINAL); + final def newImport(pos: int) = + newValue(pos, nme.IMPORT).setFlag(SYNTHETIC); + final def newOverloaded(pre: Type, alternatives: List[Symbol]): Symbol = + newValue(alternatives.head.pos, alternatives.head.name) + .setFlag(OVERLOADED) + .setInfo(OverloadedType(pre, alternatives)); + + final def newErrorValue(name: Name) = + newValue(pos, name).setFlag(SYNTHETIC | IS_ERROR).setInfo(ErrorType); + final def newAliasType(pos: int, name: Name) = + new TypeSymbol(this, pos, name); + final def newAbstractType(pos: int, name: Name) = + new TypeSymbol(this, pos, name).setFlag(DEFERRED); + final def newTypeParameter(pos: int, name: Name) = + newAbstractType(pos, name).setFlag(PARAM); + final def newTypeSkolem: Symbol = + new TypeSkolem(owner, pos, name, this) + .setFlag(flags); + final def newClass(pos: int, name: Name) = + new ClassSymbol(this, pos, name); + final def newModuleClass(pos: int, name: Name) = + new ModuleClassSymbol(this, pos, name); + final def newAnonymousClass(pos: int) = + newClass(pos, nme.ANON_CLASS_NAME.toTypeName); + final def newAnonymousFunctionClass(pos: int) = + newClass(pos, nme.ANON_FUN_NAME.toTypeName); + final def newRefinementClass(pos: int) = + newClass(pos, nme.REFINE_CLASS_NAME.toTypeName); + final def newErrorClass(name: Name) = { + val clazz = newClass(pos, name).setFlag(SYNTHETIC | IS_ERROR); + clazz.setInfo(ClassInfoType(List(), new ErrorScope(this), clazz)); + clazz + } + final def newErrorSymbol(name: Name): Symbol = + if (name.isTypeName) newErrorClass(name) else newErrorValue(name); + +// Tests ---------------------------------------------------------------------- + + def isTerm = false; //to be overridden + def isType = false; //to be overridden + def isClass = false; //to be overridden + + final def isValue = isTerm && !(isModule && hasFlag(PACKAGE | JAVA)); + final def isVariable = isTerm && hasFlag(MUTABLE) && !isMethod; + final def isCapturedVariable = isVariable && hasFlag(CAPTURED); + + final def isSetter = isTerm && hasFlag(ACCESSOR) && nme.isSetterName(name); + //todo: make independent of name, as this can be forged. + final def hasGetter = isTerm && nme.isLocalName(name); + final def isValueParameter = isTerm && hasFlag(PARAM); + final def isLocalDummy = isTerm && nme.isLocalDummyName(name); + final def isMethod = isTerm && hasFlag(METHOD); + final def isSourceMethod = isTerm && (flags & (METHOD | STABLE)) == METHOD; + final def isLabel = isTerm && hasFlag(LABEL); + final def isClassConstructor = isTerm && (name == nme.CONSTRUCTOR); + final def isMixinConstructor = isTerm && (name == nme.MIXIN_CONSTRUCTOR); + final def isConstructor = isTerm && (name == nme.CONSTRUCTOR) || (name == nme.MIXIN_CONSTRUCTOR); + final def isModule = isTerm && hasFlag(MODULE); + final def isStaticModule = isModule && isStatic && !isMethod; + final def isPackage = isModule && hasFlag(PACKAGE); + final def isThisSym = isTerm && name == nme.this_; + final def isThisSkolem = isTerm && deSkolemize != this; + final def isError = hasFlag(IS_ERROR); + final def isTrait = isClass & hasFlag(TRAIT); + final def isAliasType = isType && !isClass && !hasFlag(DEFERRED); + final def isAbstractType = isType && !isClass && hasFlag(DEFERRED); + final def isTypeParameterOrSkolem = isType && hasFlag(PARAM); + final def isTypeParameter = isTypeParameterOrSkolem && deSkolemize == this; + final def isClassLocalToConstructor = isClass && hasFlag(INCONSTRUCTOR); + final def isAnonymousClass = isClass && (originalName startsWith nme.ANON_CLASS_NAME); + // startsWith necessary because name may grow when lifted and also because of anonymous function classes + final def isRefinementClass = isClass && name == nme.REFINE_CLASS_NAME.toTypeName; // no lifting for refinement classes + final def isModuleClass = isClass && hasFlag(MODULE); + final def isPackageClass = isClass && hasFlag(PACKAGE); + final def isRoot = isPackageClass && name == nme.ROOT.toTypeName; + final def isRootPackage = isPackage && name == nme.ROOT; + final def isEmptyPackage = isPackage && name == nme.EMPTY_PACKAGE_NAME; + final def isEmptyPackageClass = isPackageClass && name == nme.EMPTY_PACKAGE_NAME.toTypeName; + + /** Does this symbol denote a stable value? */ + final def isStable = + isTerm && !hasFlag(MUTABLE) && (!hasFlag(METHOD | BYNAMEPARAM) || hasFlag(STABLE)); + + /** Does this symbol denote the primary constructor + * of its enclosing class or trait? */ + final def isPrimaryConstructor = + isConstructor && owner.primaryConstructor == this; + + /** Is this symbol an implementation class for a trait ? */ + final def isImplClass: boolean = isClass && hasFlag(IMPLCLASS); + + final def needsImplClass: boolean = + isTrait && (!hasFlag(INTERFACE) || hasFlag(lateINTERFACE)) && !isImplClass; + + final def isImplOnly: boolean = ( + hasFlag(PRIVATE) || + (owner.isImplClass || owner.isTrait) && + (hasFlag(notPRIVATE | LIFTED) && !hasFlag(ACCESSOR | SUPERACCESSOR) || + isConstructor) + ); + + /** Is this symbol a module variable ? */ + final def isModuleVar: boolean = isVariable && nme.isModuleVarName(name); + + /** Is this symbol static (i.e. with no outer instance)? */ + final def isStatic: boolean = + hasFlag(STATIC) || isRoot || owner.isStaticOwner; + + /** Does this symbol denote a class that defines static symbols? */ + final def isStaticOwner: boolean = + isPackageClass || isModuleClass && isStatic; + + /** Is this symbol final?*/ + final def isFinal: boolean = ( + hasFlag(FINAL) || + isTerm && ( + hasFlag(PRIVATE) || isLocal || owner.isClass && owner.hasFlag(FINAL | MODULE)) + ); + + /** Is this symbol a sealed class?*/ + final def isSealed: boolean = + isClass && (hasFlag(SEALED) || isUnboxedClass(this)); + + /** Is this symbol locally defined? I.e. not accessed from outside `this' instance */ + final def isLocal: boolean = owner.isTerm; + + /** Is this symbol a constant? */ + final def isConstant: boolean = + isStable && (tpe match { + case ConstantType(_) => true + case PolyType(_, ConstantType(_)) => true + case MethodType(_, ConstantType(_)) => true + case _ => false + }); + + /** Is this class nested in another class or module (not a package)? */ + final def isNestedClass: boolean = + isClass && !isRoot && !owner.isPackageClass; + + /** Is this class locally defined? + * A class is local, if + * - it is anonymous, or + * - its owner is a value + * - it is defined within a local class214 + */ + final def isLocalClass: boolean = + isClass && (isAnonymousClass || isRefinementClass || isLocal || + !owner.isPackageClass && owner.isLocalClass); + + /** Symbol was preloaded from package */ + final def isExternal: boolean = rawpos == Position.NOPOS; + + /** A 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 inclomplete. + */ + final def isIncompleteIn(base: Symbol): boolean = ( + (this hasFlag DEFERRED) || + (this hasFlag ABSOVERRIDE) && { + val supersym = superSymbol(base); + supersym == NoSymbol || supersym.isIncompleteIn(base) + } + ); + + final def isInitialized: boolean = + validForRun == currentRun; + + /** The variance of this symbol as an integer */ + final def variance: int = + if (hasFlag(COVARIANT)) 1 + else if (hasFlag(CONTRAVARIANT)) -1 + else 0; + +// Flags, owner, and name attributes -------------------------------------------------------------- + + def owner: Symbol = rawowner; + final def owner_=(owner: Symbol): unit = { rawowner = owner } + + def ownerChain: List[Symbol] = this :: owner.ownerChain; + + def name: Name = rawname; + final def name_=(name: Name): unit = { rawname = name } + + def originalName = nme.originalName(name); + + final def flags = { + val fs = rawflags & phase.flagMask; + (fs | ((fs & LateFlags) >>> LateShift)) & ~(fs >>> AntiShift) + } + final def flags_=(fs: long) = rawflags = fs; + final def setFlag(mask: long): this.type = { rawflags = rawflags | mask; this } + final def resetFlag(mask: long): this.type = { rawflags = rawflags & ~mask; this } + final def getFlag(mask: long): long = flags & mask; + final def hasFlag(mask: long): boolean = (flags & mask) != 0; + final def resetFlags: unit = { rawflags = rawflags & TopLevelCreationFlags } + +// Info and Type ------------------------------------------------------------------- + + private var infos: TypeHistory = null; + private var limit: Phase#Id = 0; + + /** Get type. The type of a symbol is: + * for a type symbol, the type corresponding to the symbol itself + * for a term symbol, its usual type + */ + def tpe: Type = info; + + /** Get type info associated with symbol at current phase, after + * ensuring that symbol is initialized (i.e. type is completed). + */ + final def info: Type = { + var cnt = 0; + while (validForRun != currentRun) { + //if (settings.debug.value) System.out.println("completing " + this);//DEBUG + var ifs = infos; + assert(ifs != null, this.name); + while (ifs.prev != null) { + ifs = ifs.prev; + } + val tp = ifs.info; + //if (settings.debug.value) System.out.println("completing " + this.rawname + tp.getClass());//debug + if ((rawflags & LOCKED) != 0) { + setInfo(ErrorType); + throw CyclicReference(this, tp); + } + rawflags = rawflags | LOCKED; + val current = phase; + try { + phase = phaseWithId(ifs.start); + tp.complete(this); + // if (settings.debug.value && (validForRun == currentRun) System.out.println("completed " + this/* + ":" + info*/);//DEBUG + rawflags = rawflags & ~LOCKED + } finally { + phase = current + } + cnt = cnt + 1; + // allow for two completions: + // one: sourceCompleter to LazyType, two: LazyType to completed type + if (cnt == 3) throw new Error("no progress in completing " + this + ":" + tp); + } + rawInfo + } + + /** Set initial info. */ + def setInfo(info: Type): this.type = { + assert(info != null); + var pid = phase.id; + if (pid == 0) { + // can happen when we initialize NoSymbol before running the compiler + assert(name == nme.NOSYMBOL); + pid = 1 + } + infos = new TypeHistory(pid, info, null); + limit = pid; + if (info.isComplete) { + rawflags = rawflags & ~LOCKED; + validForRun = currentRun + } else { + rawflags = rawflags & ~LOCKED; + validForRun = NoRun + } + this + } + + /** Set new info valid from start of this phase. */ + final def updateInfo(info: Type): Symbol = { + assert(infos.start <= phase.id); + if (infos.start == phase.id) infos = infos.prev; + infos = new TypeHistory(phase.id, info, infos); + this + } + + /** Return info without checking for initialization or completing */ + final def rawInfo: Type = { + if (limit < phase.id) { + if (validForRun == currentRun) { + val current = phase; + var itr = infoTransformers.nextFrom(limit); + infoTransformers = itr; // caching optimization + while (itr.pid != NoPhase.id && itr.pid < current.id) { + phase = phaseWithId(itr.pid); + val info1 = itr.transform(this, infos.info); + limit = phase.id + 1; + if (info1 ne infos.info) { + infos = new TypeHistory(limit, info1, infos); + } + itr = itr.nextFrom(limit) + } + phase = current; + limit = current.id; + } + assert(infos != null, name); + infos.info + } else { + var infos = this.infos; + while (phase.id < infos.start && infos.prev != null) infos = infos.prev; + infos.info + } + } + + /** Initialize the symbol */ + final def initialize: this.type = { + if (!isInitialized) info; + this + } + + /** Was symbol's type updated during given phase? */ + final def isUpdatedAt(pid: Phase#Id): boolean = { + var infos = this.infos; + while (infos != null && infos.start != pid + 1) infos = infos.prev; + infos != null + } + + /** The type constructor of a symbol is: + * For a type symbol, the type corresponding to the symbol itself, + * excluding parameters. + * Not applicable for term symbols. + */ + def typeConstructor: Type = throw new Error("typeConstructor inapplicable for " + this); + + /** The type parameters of this symbol */ + def unsafeTypeParams: List[Symbol] = rawInfo.typeParams; + + def typeParams: List[Symbol] = { + rawInfo.load(this); rawInfo.typeParams + } + + /** Reset symbol to initial state + */ + def reset(completer: Type): unit = { + resetFlags; + rawpos = Position.NOPOS; + infos = null; + limit = NoPhase.id; + setInfo(completer) + } + +// Comparisons ---------------------------------------------------------------- + + /** A total ordering between symbols that refines the class + * inheritance graph (i.e. subclass.isLess(superclass) always holds). + * the ordering is given by: (isType, -|closure| for type symbols, id) + */ + final def isLess(that: Symbol): boolean = { + def closureLength(sym: Symbol) = + if (sym.isAbstractType) 1 + sym.info.bounds.hi.closure.length + else sym.info.closure.length; + if (this.isType) + (that.isType && + { val diff = closureLength(this) - closureLength(that); + diff > 0 || diff == 0 && this.id < that.id }) + else + that.isType || this.id < that.id; + } + + /** A partial ordering between symbols. + * (this isNestedIn that) holds iff this symbol is defined within + * a class or method defining that symbol + */ + final def isNestedIn(that: Symbol): boolean = + owner == that || owner != NoSymbol && (owner isNestedIn that); + + /** Is this class symbol a subclass of that symbol? */ + final def isSubClass(that: Symbol): boolean = ( + this == that || this.isError || that.isError || + info.closurePos(that) >= 0 || + this == AllClass || + this == AllRefClass && + (that == AnyClass || + that != AllClass && (that isSubClass AnyRefClass)) + ); + +// Overloaded Alternatives --------------------------------------------------------- + + def alternatives: List[Symbol] = + if (hasFlag(OVERLOADED)) info.asInstanceOf[OverloadedType].alternatives + else List(this); + + def filter(cond: Symbol => boolean): Symbol = + if (hasFlag(OVERLOADED)) { + //assert(info.isInstanceOf[OverloadedType], "" + this + ":" + info);//DEBUG + val alts = alternatives; + val alts1 = alts filter cond; + if (alts1 eq alts) this + else if (alts1.isEmpty) NoSymbol + else if (alts1.tail.isEmpty) alts1.head + else owner.newOverloaded(info.prefix, alts1) + } else if (cond(this)) this + else NoSymbol; + + def suchThat(cond: Symbol => boolean): Symbol = { + val result = filter(cond); + assert(!(result hasFlag OVERLOADED), result.alternatives); + result + } + +// Cloneing ------------------------------------------------------------------- + + /** A clone of this symbol */ + final def cloneSymbol: Symbol = + cloneSymbol(owner); + + /** A clone of this symbol, but with given owner */ + final def cloneSymbol(owner: Symbol): Symbol = + cloneSymbolImpl(owner).setInfo(info.cloneInfo(owner)).setFlag(this.rawflags); + + /** Internal method to clone a symbol's implementation without flags or type + */ + def cloneSymbolImpl(owner: Symbol): Symbol; + +// Access to related symbols -------------------------------------------------- + + /** The next enclosing class */ + def enclClass: Symbol = if (isClass) this else owner.enclClass; + + /** The next enclosing method */ + def enclMethod: Symbol = if (isSourceMethod) this else owner.enclMethod; + + /** The primary constructor of a class */ + def primaryConstructor: Symbol = { + val c = info.decl(if (isTrait || isImplClass) nme.MIXIN_CONSTRUCTOR else nme.CONSTRUCTOR); + if (c hasFlag OVERLOADED) c.alternatives.head else c + } + + /** The self symbol of a class with explicit self type, or else the symbol itself. + */ + def thisSym: Symbol = this; + + /** The type of `this' in a class, or else the type of the symbol itself. */ + final def typeOfThis = thisSym.tpe; + + /** Sets the type of `this' in a class */ + def typeOfThis_=(tp: Type): unit = throw new Error("typeOfThis cannot be set for " + this); + + /** If symbol is a class, the type this.type in this class, otherwise NoPrefix */ + def thisType: Type = NoPrefix; + + /** Return every accessor of a primary constructor parameter in this case class + * todo: limit to accessors for first constructor parameter section. + */ + final def caseFieldAccessors: List[Symbol] = + info.decls.toList filter (sym => !(sym hasFlag PRIVATE) && sym.hasFlag(CASEACCESSOR)); + + final def constrParamAccessors: List[Symbol] = + info.decls.toList filter (sym => !sym.isMethod && sym.hasFlag(PARAMACCESSOR)); + + /** The symbol accessed by this accessor function. + */ + final def accessed: Symbol = { + assert(hasFlag(ACCESSOR)); + owner.info.decl(nme.getterToLocal(if (isSetter) nme.setterToGetter(name) else name)) + } + + final def implClass: Symbol = owner.info.decl(nme.implClassName(name)); + + /** For a paramaccessor: a superclass paramaccessor for which this symbol is + * an alias, NoSymbol for all others */ + def alias: Symbol = NoSymbol; + + /** The class with the same name in the same package as this module or + * case class factory + */ + final def linkedClass: Symbol = { + if (owner.isPackageClass) + owner.info.decl(name.toTypeName).suchThat(sym => sym.rawInfo ne NoType) + else NoSymbol; + } + + /** The module or case class factory with the same name in the same + * package as this class. + */ + final def linkedModule: Symbol = + if (owner.isPackageClass) + owner.info.decl(name.toTermName).suchThat( + sym => (sym hasFlag MODULE) && (sym.rawInfo ne NoType)); + else NoSymbol; + + /** The top-level class containing this symbol */ + def toplevelClass: Symbol = + if (isClass && owner.isPackageClass) this else owner.toplevelClass; + + /** For a module its linked class, for a class its linked module or case factory otherwise */ + final def linkedSym: Symbol = + if (isTerm) linkedClass + else if (isClass && owner.isPackageClass) + owner.info.decl(name.toTermName).suchThat(sym => sym.rawInfo ne NoType) + else NoSymbol; + + final def toInterface: Symbol = + if (isImplClass) { + assert(!tpe.parents.isEmpty, this); + tpe.parents.last.symbol + } else this; + + /** The module corresponding to this module class (note that this + * is not updated when a module is cloned). + */ + def sourceModule: Symbol = NoSymbol; + + /** The module class corresponding to this module. + */ + def moduleClass: Symbol = NoSymbol; + + /** The non-abstract, symbol whose type matches the type of this symbol in in given class + * @param ofclazz The class containing the symbol's definition + * @param site The base type from which member types are computed + */ + final def matchingSymbol(ofclazz: Symbol, site: Type): Symbol = + ofclazz.info.nonPrivateDecl(name).suchThat(sym => + !sym.isTerm || (site.memberType(this) matches site.memberType(sym))); + + /** The symbol overridden by this symbol in given class `ofclazz' */ + final def overriddenSymbol(ofclazz: Symbol): Symbol = + matchingSymbol(ofclazz, owner.thisType); + + /** The symbol overriding this symbol in given subclass `ofclazz' */ + final def overridingSymbol(ofclazz: Symbol): Symbol = + matchingSymbol(ofclazz, ofclazz.thisType); + + /** 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 superSymbol(base: Symbol): Symbol = { + var bcs = base.info.baseClasses.dropWhile(owner !=).tail; + var sym: Symbol = NoSymbol; + while (!bcs.isEmpty && sym == NoSymbol) { + if (!bcs.head.isImplClass) + sym = matchingSymbol(bcs.head, base.thisType).suchThat( + sym => !sym.hasFlag(DEFERRED)); + bcs = bcs.tail + } + sym + } + + /** The getter of this value definition in class `base', or NoSymbol if none exists */ + final def getter(base: Symbol): Symbol = + base.info.decl(nme.getterName(name)) filter (.hasFlag(ACCESSOR)); + + /** The setter of this value definition, or NoSymbol if none exists */ + final def setter(base: Symbol): Symbol = + base.info.decl(nme.getterToSetter(nme.getterName(name))) filter (.hasFlag(ACCESSOR)); + + /** If this symbol is a skolem, its corresponding type parameter, otherwise this */ + def deSkolemize: Symbol = this; + + /** Remove private modifier from symbol `sym's definition. If `sym' is a + * term symbol rename it by expanding its name to avoid name clashes + */ + final def makeNotPrivate(base: Symbol): unit = + if (isTerm && (this hasFlag PRIVATE)) { + setFlag(notPRIVATE); + if (!hasFlag(DEFERRED)) setFlag(lateFINAL); + expandName(base) + } + + /** change name by appending $$<fully-qualified-name-of-class `base'> + * Do the same for any accessed symbols or setters/getters + */ + def expandName(base: Symbol): unit = + if (this != NoSymbol && !hasFlag(EXPANDEDNAME)) { + setFlag(EXPANDEDNAME); + if (hasFlag(ACCESSOR)) { + accessed.expandName(base); + } else if (hasGetter) { + getter(owner).expandName(base); + setter(owner).expandName(base); + } + name = base.expandedName(name) + } + + def expandedName(name: Name): Name = + newTermName(fullNameString('$') + nme.EXPAND_SEPARATOR_STRING + name); + +/* + def referenced: Symbol = + throw new Error("referenced inapplicable for " + this); + + def setReferenced(sym: Symbol): Symbol = + throw new Error("setReferenced inapplicable for " + this); +*/ +// ToString ------------------------------------------------------------------- + + /** A tag which (in the ideal case) uniquely identifies class symbols */ + final def tag: int = fullNameString.hashCode(); + + /** The simple name of this Symbol (this is always a term name) */ + final def simpleName: Name = name; + + /** String representation of symbol's definition key word */ + final def keyString: String = + if (isTrait) + if (hasFlag(JAVA)) "interface" else "trait" + else if (isClass) "class" + else if (isType && !hasFlag(PARAM)) "type" + else if (isVariable) "var" + else if (isPackage) "package" + else if (isModule) "object" + else if (isMethod) "def" + else if (isTerm && (!hasFlag(PARAM) || hasFlag(PARAMACCESSOR))) "val" + else ""; + + /** String representation of symbol's kind */ + final def kindString: String = + if (isPackageClass) + if (settings.debug.value) "package class" else "package" + else if (isAnonymousClass) "<template>" + else if (isRefinementClass) "" + else if (isModuleClass) "singleton class" + else if (isTrait) "trait" + else if (isClass) "class" + else if (isType) "type" + else if (isVariable) "variable" + else if (isPackage) "package" + else if (isModule) "object" + else if (isClassConstructor) "constructor" + else if (isSourceMethod) "method" + else if (isTerm) "value" + else ""; + + /** String representation of symbol's simple name. + * If !settings.debug translates expansions of operators back to operator symbol. + * E.g. $eq => =. + * If settings.uniquId adds id. + */ + def nameString: String = //todo: should be final + simpleName.decode + idString; + + /** String representation of symbol's full name with `separator' + * between class names. + * Never translates expansions of operators back to operator symbol. + * Never adds id. + */ + final def fullNameString(separator: char): String = + if (owner.isRoot || owner.isEmptyPackageClass) simpleName.toString() + else owner.fullNameString(separator) + separator + simpleName; + + final def fullNameString: String = fullNameString('.'); + + /** If settings.uniqid is set, the symbol's id, else "" */ + final def idString: String = + if (settings.uniqid.value) "#" + id else ""; + + /** String representation, including symbol's kind + * e.g., "class Foo", "method Bar". + */ + override def toString(): String = + compose(List(kindString, if (isClassConstructor) owner.nameString else nameString)); + + /** String representation of location. */ + final def locationString: String = + if (owner.isClass && + (!owner.isAnonymousClass && !owner.isRefinementClass || settings.debug.value)) + " in " + (if (owner.isModuleClass) "object " + owner.nameString else owner) + else ""; + + /** String representation of symbol's definition following its name */ + final def infoString(tp: Type): String = { + def typeParamsString: String = tp match { + case PolyType(tparams, _) if (tparams.length != 0) => + (tparams map (.defString)).mkString("[", ",", "]") + case _ => + "" + } + if (isClass) + typeParamsString + " extends " + tp.resultType + else if (isAliasType) + typeParamsString + " = " + tp.resultType + else if (isAbstractType) + tp match { + case TypeBounds(lo, hi) => + ((if (lo.symbol == AllClass) "" else " >: " + lo) + + (if (hi.symbol == AnyClass) "" else " <: " + hi)) + case _ => + "<: " + tp; + } + else if (isModule) + moduleClass.infoString(tp) + else + tp match { + case PolyType(tparams, res) => + typeParamsString + infoString(res) + case MethodType(pts, res) => + pts.mkString("(", ",", ")") + infoString(res) + case _ => + ": " + tp + } + } + + def infosString = infos.toString(); + + /** String representation of symbol's variance */ + private def varianceString: String = + if (variance == 1) "+" + else if (variance == -1) "-" + else ""; + + /** String representation of symbol's definition */ + final def defString: String = + compose(List(flagsToString(if (settings.debug.value) flags else flags & ExplicitFlags), + keyString, + varianceString + nameString + infoString(rawInfo))); + + /** Concatenate strings separated by spaces */ + private def compose(ss: List[String]): String = + ss.filter("" !=).mkString("", " ", ""); + } + + /** A class for term symbols */ + class TermSymbol(initOwner: Symbol, initPos: int, initName: Name) extends Symbol(initOwner, initPos, initName) { + override def isTerm = true; + + protected var referenced: Symbol = NoSymbol; + + def cloneSymbolImpl(owner: Symbol): Symbol = { + val clone = new TermSymbol(owner, pos, name); + clone.referenced = referenced; + clone + } + + override def alias: Symbol = + if (hasFlag(SUPERACCESSOR | PARAMACCESSOR | MIXEDIN)) initialize.referenced else NoSymbol; + + def setAlias(alias: Symbol): TermSymbol = { + assert(alias != NoSymbol); + assert(hasFlag(SUPERACCESSOR | PARAMACCESSOR | MIXEDIN)); + referenced = alias; + this + } + + override def moduleClass: Symbol = + if (hasFlag(MODULE)) referenced else NoSymbol; + + def setModuleClass(clazz: Symbol): TermSymbol = { + assert(hasFlag(MODULE)); + referenced = clazz; + this + } + } + + /** A class for term symbols */ + class ModuleSymbol(initOwner: Symbol, initPos: int, initName: Name) extends TermSymbol(initOwner, initPos, initName) { + + private var flatname = nme.EMPTY; + + override def owner: Symbol = + if (phase.flatClasses && !hasFlag(METHOD) && + rawowner != NoSymbol && !rawowner.isPackageClass) rawowner.owner + else rawowner; + + override def name: Name = + if (phase.flatClasses && !hasFlag(METHOD) && + rawowner != NoSymbol && !rawowner.isPackageClass) { + if (flatname == nme.EMPTY) { + assert(rawowner.isClass); + flatname = newTermName(rawowner.name.toString() + "$" + rawname); + } + flatname + } else rawname; + + override def cloneSymbolImpl(owner: Symbol): Symbol = { + val clone = new ModuleSymbol(owner, pos, name); + clone.referenced = referenced; + clone + } + } + + /** A class for type parameters viewed from inside their scopes */ + class ThisSkolem(initOwner: Symbol, initPos: int, initName: Name, clazz: Symbol) extends TermSymbol(initOwner, initPos, initName) { + override def deSkolemize = clazz; + override def cloneSymbolImpl(owner: Symbol): Symbol = { + throw new Error("should not clone a this skolem"); + } + override def nameString: String = clazz.name.toString() + ".this"; + } + + /** A class of type symbols. Alias and abstract types are direct instances + * of this class. Classes are instances of a subclass. + */ + class TypeSymbol(initOwner: Symbol, initPos: int, initName: Name) extends Symbol(initOwner, initPos, initName) { + override def isType = true; + private var tyconCache: Type = null; + private var tyconRun: CompilerRun = null; + private var tpeCache: Type = _; + private var tpePhase: Phase = null; + override def tpe: Type = { + if (tpeCache eq NoType) throw CyclicReference(this, typeConstructor); + if (tpePhase != phase) { + if (isValid(tpePhase)) { + tpePhase = phase + } else { + if (isInitialized) tpePhase = phase; + tpeCache = NoType; + val targs = if (phase.erasedTypes && this != ArrayClass) List() + else unsafeTypeParams map (.tpe); + tpeCache = typeRef(if (isTypeParameterOrSkolem) NoPrefix else owner.thisType, this, targs) + } + } + assert(tpeCache != null/*, "" + this + " " + phase*/);//debug + tpeCache + } + + override def typeConstructor: Type = { + if (tyconCache == null || tyconRun != currentRun) { + tyconCache = typeRef(if (isTypeParameter) NoPrefix else owner.thisType, this, List()); + tyconRun = currentRun; + } + assert(tyconCache != null); + tyconCache + } + + override def setInfo(tp: Type): this.type = { + tpePhase = null; + tyconCache = null; + tp match { //debug + case TypeRef(_, sym, _) => + assert(sym != this, this); + case _ => + } + super.setInfo(tp); + this + } + + override def reset(completer: Type): unit = { + super.reset(completer); + tpePhase = null; + tyconCache = null; + } + + def cloneSymbolImpl(owner: Symbol): Symbol = + new TypeSymbol(owner, pos, name); + + if (util.Statistics.enabled) typeSymbolCount = typeSymbolCount + 1; + } + + /** A class for type parameters viewed from inside their scopes */ + class TypeSkolem(initOwner: Symbol, initPos: int, initName: Name, typeParam: Symbol) extends TypeSymbol(initOwner, initPos, initName) { + override def deSkolemize = typeParam; + override def cloneSymbolImpl(owner: Symbol): Symbol = { + throw new Error("should not clone a type skolem"); + } + override def nameString: String = super.nameString + "&"; + } + + /** A class for class symbols */ + class ClassSymbol(initOwner: Symbol, initPos: int, initName: Name) extends TypeSymbol(initOwner, initPos, initName) { + + + var sourceFile: AbstractFile = null; + private var thissym: Symbol = this; + override def isClass: boolean = true; + override def reset(completer: Type): unit = { + super.reset(completer); + thissym = this; + } + + private var flatname = nme.EMPTY; + + override def owner: Symbol = + if (phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass) rawowner.owner + else rawowner; + + override def name: Name = + if (phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass) { + if (flatname == nme.EMPTY) { + assert(rawowner.isClass); + flatname = newTypeName(rawowner.name.toString() + "$" + rawname); + } + flatname + } else rawname; + + private var thisTypeCache: Type = _; + private var thisTypePhase: Phase = null; + + /** the type this.type in this class */ + override def thisType: Type = { + val p = thisTypePhase; + if (p != phase) { + thisTypePhase = phase; + if (!(isValid(p) /*|| + thisTypePhase != null && thisTypePhase.erasedTypes && phase.erasedTypes*/)) { + thisTypeCache = ThisType(this) +/* + if (isModuleClass && !isRoot && !phase.erasedTypes) + singleType(owner.thisType, sourceModule); + else ThisType(this); +*/ + } + } + thisTypeCache + } + + /** A symbol carrying the self type of the class as its type */ + override def thisSym: Symbol = thissym; + + /** Sets the self type of the class */ + override def typeOfThis_=(tp: Type): unit = + thissym = newThisSym(pos).setInfo(tp); + + override def cloneSymbolImpl(owner: Symbol): Symbol = { + assert(!isModuleClass); + val clone = new ClassSymbol(owner, pos, name); + if (thisSym != this) clone.typeOfThis = typeOfThis; + clone + } + + override def sourceModule = if (isModuleClass) linkedModule else NoSymbol; + + if (util.Statistics.enabled) classSymbolCount = classSymbolCount + 1; + } + + /** A class for module class symbols + * Note: Not all module classes are of this type; when unpickled, we get plain class symbols! + */ + class ModuleClassSymbol(owner: Symbol, pos: int, name: Name) extends ClassSymbol(owner, pos, name) { + private var module: Symbol = null; + def this(module: TermSymbol) = { + this(module.owner, module.pos, module.name.toTypeName); + setFlag(module.getFlag(ModuleToClassFlags) | MODULE | FINAL); + setSourceModule(module); + } + override def sourceModule = module; + def setSourceModule(module: Symbol): unit = this.module = module + } + + /** An object repreesenting a missing symbol */ + object NoSymbol extends Symbol(null, Position.NOPOS, nme.NOSYMBOL) { + setInfo(NoType); + override def setInfo(info: Type): this.type = { assert(info eq NoType); super.setInfo(info) } + override def enclClass: Symbol = this; + override def toplevelClass: Symbol = this; + override def enclMethod: Symbol = this; + override def owner: Symbol = throw new Error(); + override def ownerChain: List[Symbol] = List(); + override def alternatives: List[Symbol] = List(); + override def reset(completer: Type): unit = {} + def cloneSymbolImpl(owner: Symbol): Symbol = throw new Error(); + } + + def cloneSymbols(syms: List[Symbol]): List[Symbol] = { + val syms1 = syms map (.cloneSymbol); + for (val sym1 <- syms1) sym1.setInfo(sym1.info.substSym(syms, syms1)); + syms1 + } + + def cloneSymbols(syms: List[Symbol], owner: Symbol): List[Symbol] = { + val syms1 = syms map (.cloneSymbol(owner)); + for (val sym1 <- syms1) sym1.setInfo(sym1.info.substSym(syms, syms1)); + syms1 + } + + /** An exception for cyclic references of symbol definitions */ + case class CyclicReference(sym: Symbol, info: Type) extends TypeError("illegal cyclic reference involving " + sym); + + /** A class for type histories */ + private case class TypeHistory(start: Phase#Id, info: Type, prev: TypeHistory) { + assert(prev == null || start > prev.start, this); + assert(start != 0); + override def toString() = "TypeHistory(" + phaseWithId(start) + "," + info + "," + prev + ")"; + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala new file mode 100644 index 0000000000..583ca6503c --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -0,0 +1,2060 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab; + +import scala.tools.nsc.util.Position; +import nsc.util.{ListBuffer, HashSet}; +import Flags._; + +/* A standard type pattern match: + case ErrorType => + case WildcardType => + case NoType => + case NoPrefix => + case ThisType(_) => + case SingleType(pre, sym) => + case ConstantType(value) => + case TypeRef(pre, sym, args) => + case TypeBounds(lo, hi) => + case RefinedType(parents, defs) => + case ClassInfoType(parents, defs, clazz) => + case MethodType(paramtypes, result) => + case PolyType(tparams, result) => + // the last three types are not used after phase `typer'. + case OverloadedType(pre, tparams, alts) => + case AntiPolyType(pre: Type, targs) => + case TypeVar(_, _) => +*/ + +[_trait_] abstract class Types: SymbolTable { + import definitions._; + + //statistics + var singletonClosureCount = 0; + var compoundClosureCount = 0; + var typerefClosureCount = 0; + var findMemberCount = 0; + var noMemberCount = 0; + var multMemberCount = 0; + var findMemberMillis = 0l; + var subtypeCount = 0; + var subtypeMillis = 0l; + + private var explainSwitch = false; + private var checkMalformedSwitch = true; + + val emptyTypeArray = new Array[Type](0); + + /** The base class for all types */ + abstract class Type { + + /** Types for which asSeenFrom always is the identity, no matter what prefix or owner */ + def isTrivial: boolean = false; + + /** The symbol associated with the type */ + def symbol: Symbol = NoSymbol; + + /** The base type underlying a singleton type, + * identity on all other types */ + def singleDeref: Type = this; + + /** Widen from singleton type to its underlying non-singleton base type + * by applying one or more singleDeref steps, + * identity for all other types */ + def widen: Type = this; + + /** The type of `this' of a class type or reference type + */ + def typeOfThis = symbol.typeOfThis; + + /** Map to a this type which is a subtype of this type. + */ + def narrow: Type = + if (phase.erasedTypes) this + else refinedType(List(this), commonOwner(this), EmptyScope).narrow; + + /** Map a constant type to its underlying base type, + * identity for all other types */ + def deconst: Type = this; + + /** For a TypeBounds type, itself; + * for a reference denoting an abstract type, its bounds, + * for all other types, a TypeBounds type all of whose bounds are this type. + * error for all other types */ + def bounds: TypeBounds = TypeBounds(this, this); + + /** For a class or intersection type, its parents. + * For a TypeBounds type, the parents of its hi bound. + * inherited by typerefs, singleton types, and refinement types, + * The empty list for all other types */ + def parents: List[Type] = List(); + + /** For a typeref or single-type, its prefix. NoType for all other types. */ + def prefix: Type = NoType; + + /** For a typeref, its arguments. The empty list for all other types */ + def typeArgs: List[Type] = List(); + + /** For a method or poly type, its direct result type, + * the type itself for all other types */ + def resultType: Type = this; + + /** For a curried method or poly type its non-method result type, + * the type itself for all other types */ + def finalResultType: Type = this; + + /** For a method or poly type, the number of its value parameter sections, + * 0 for all other types */ + def paramSectionCount: int = 0; + + /** For a method or poly type, the types of its first value parameter section, + * the empty list for all other types */ + def paramTypes: List[Type] = List(); + + /** For a poly type, its type parameters, + * the empty list for all other types */ + def typeParams: List[Symbol] = List(); + + /** Is this type produced as a repair for an error? */ + def isError: boolean = symbol.isError; + + /** Does this type denote a stable reference (i.e. singleton type)? */ + def isStable: boolean = false; + + /** For a classtype or refined type, its defined or declared members; + * inherited by subtypes and typerefs. + * The empty scope for all other types */ + def decls: Scope = EmptyScope; + + /** The defined or declared members with name `name' in this type; + * an OverloadedSymbol if several exist, NoSymbol if none exist. + * Alternatives of overloaded symbol appear in the order they are declared. + */ + def decl(name: Name): Symbol = findDecl(name, 0); + + /** The non-private defined or declared members with name `name' in this type; + * an OverloadedSymbol if several exist, NoSymbol if none exist. + * Alternatives of overloaded symbol appear in the order they are declared. + */ + def nonPrivateDecl(name: Name): Symbol = findDecl(name, PRIVATE); + + /** A list of all members of this type (defined or inherited) + * Members appear in linearization order of their owners. + * Members with the same owner appear in reverse order of their declarations. + */ + def members: List[Symbol] = findMember(nme.ANYNAME, 0, 0).alternatives; + + /** A list of all non-private members of this type (defined or inherited) */ + def nonPrivateMembers: List[Symbol] = findMember(nme.ANYNAME, PRIVATE | BRIDGE, 0).alternatives; + + /** A list of all implicit symbols of this type (defined or inherited) */ + def implicitMembers: List[Symbol] = findMember(nme.ANYNAME, BRIDGE, IMPLICIT).alternatives; + + /** The member with given name, + * an OverloadedSymbol if several exist, NoSymbol if none exist */ + def member(name: Name): Symbol = findMember(name, BRIDGE, 0); + + /** The non-private member with given name, + * an OverloadedSymbol if several exist, NoSymbol if none exist */ + def nonPrivateMember(name: Name): Symbol = findMember(name, PRIVATE | BRIDGE, 0); + + /** The non-local member with given name, + * an OverloadedSymbol if several exist, NoSymbol if none exist */ + def nonLocalMember(name: Name): Symbol = findMember(name, LOCAL | BRIDGE, 0); + + /** The least type instance of given class which is a supertype + * of this type */ + def baseType(clazz: Symbol): Type = NoType; + + /** This type as seen from prefix ` + pre' and class `clazz'. This means: + * Replace all thistypes of `clazz' or one of its subclasses by `pre' + * and instantiate all parameters by arguments of `pre'. + * Proceed analogously for thistypes referring to outer classes. */ + def asSeenFrom(pre: Type, clazz: Symbol): Type = + if (!isTrivial && (!phase.erasedTypes || pre.symbol == ArrayClass)) { + new AsSeenFromMap(pre, clazz) apply this; + } else this; + + /** The info of `sym', seen as a member of this type. */ + def memberInfo(sym: Symbol): Type = + sym.info.asSeenFrom(this, sym.owner); + + /** The type of `sym', seen as a memeber of this type. */ + def memberType(sym: Symbol): Type = { + val result = sym.tpe.asSeenFrom(this, sym.owner); + /*System.out.println("" + this + ".memberType(" + sym + ") = " + result);*/ + result + } + + /** Substitute types `to' for occurrences of references to symbols `from' + * in this type. */ + def subst(from: List[Symbol], to: List[Type]): Type = + new SubstTypeMap(from, to) apply this; + + /** Substitute symbols `to' for occurrences of symbols `from' in this type. */ + def substSym(from: List[Symbol], to: List[Symbol]): Type = + new SubstSymMap(from, to) apply this; + + /** Substitute all occurrences of ThisType(from) in this type by `to' */ + def substThis(from: Symbol, to: Type): Type = + new SubstThisMap(from, to) apply this; + + def substSuper(from: Type, to: Type): Type = + new SubstSuperMap(from, to) apply this; + + /** Does this type contain a reference to this symbol? */ + def contains(sym: Symbol): boolean = + new ContainsTraverser(sym).traverse(this).result; + + /** Is this type a subtype of that type? */ + def <:<(that: Type): boolean = { + if (util.Statistics.enabled) subtypeCount = subtypeCount + 1; + val startTime = if (util.Statistics.enabled) System.currentTimeMillis() else 0l; + val result = + ((this eq that) || + (if (explainSwitch) explain("<", isSubType, this, that) else isSubType(this, that))); + if (util.Statistics.enabled) subtypeMillis = subtypeMillis + System.currentTimeMillis() - startTime; + result + } + + /** Is this type equivalent to that type? */ + def =:=(that: Type): boolean = ( + (this eq that) || + (if (explainSwitch) explain("=", isSameType, this, that) else isSameType(this, that)) + ); + + /** Does this type implement symbol `sym' with same or stronger type? */ + def specializes(sym: Symbol): boolean = + if (explainSwitch) explain("specializes", specializesSym, this, sym) + else specializesSym(this, sym); + + /** Is this type close enough to that type so that + * members with the two type 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 type parameter types + * and matching result types + * - Or both types are equivalent + * - Or phase.erasedTypes is false and both types are neither method nor + * poly types. + */ + def matches(that: Type): boolean = matchesType(this, that); + + /** The shortest sorted upwards closed array of types that contains + * this type as first element. + * + * A list or array of types ts is upwards closed if + * + * for all t in ts: + * for all typerefs p.s[args] such that t <: p.s[args] + * there exists a typeref p'.s[args'] in ts such that + * t <: p'.s['args] <: p.s[args], + * and + * for all singleton types p.s such that t <: p.s + * there exists a singleton type p'.s in ts such that + * t <: p'.s <: p.s + * + * Sorting is with respect to Symbol.isLess() on type symbols. + */ + def closure: Array[Type] = Predef.Array(this); + + def baseClasses: List[Symbol] = List(); + + /** The index of given class symbol in the closure of this type, + * or -1 if no base type with given class symbol exists */ + def closurePos(sym: Symbol): int = { + val cl = closure; + var lo = 0; + var hi = cl.length - 1; + while (lo <= hi) { + val mid = (lo + hi) / 2; + val clsym = cl(mid).symbol; + if (sym == clsym) return mid + else if (sym isLess clsym) hi = mid - 1 + else if (clsym isLess sym) lo = mid + 1 + else throw new Error() + } + -1 + } + + /** If this is a polytype, a copy with cloned type parameters owned + * by `owner'. Identity for all other types. */ + def cloneInfo(owner: Symbol) = this; + + /** The string representation of this type used as a prefix */ + def prefixString = toString() + "#"; + + /** The string representation of this type, with singletypes explained */ + def toLongString = { + val str = toString(); + if (str.endsWith(".type")) str + " (with underlying type " + widen + ")"; + else str + } + + /** Is this type completed (i.e. not a lazy type)? + */ + def isComplete: boolean = true; + + /** If this is a lazy type, assign a new type to `sym'. */ + def complete(sym: Symbol): unit = { + if (sym == NoSymbol || sym.isPackageClass) sym.validForRun = currentRun + else { + val this1 = adaptToNewRunMap(this); + if (this1 eq this) sym.validForRun = currentRun + else { + //System.out.println("new type of " + sym + "=" + this1);//DEBUG + sym.setInfo(this1); + } + } + } + + /** If this is a symbol loader type, load and assign a new type to `sym'. */ + def load(sym: Symbol): unit = {} + + private def findDecl(name: Name, excludedFlags: int): Symbol = { + var alts: List[Symbol] = List(); + var sym: Symbol = NoSymbol; + var e: ScopeEntry = decls.lookupEntry(name); + while (e != null) { + if (!e.sym.hasFlag(excludedFlags)) { + if (sym == NoSymbol) sym = e.sym + else { + if (alts.isEmpty) alts = List(sym); + alts = e.sym :: alts + } + } + e = decls.lookupNextEntry(e) + } + if (alts.isEmpty) sym + else baseClasses.head.newOverloaded(this, alts) + } + + //todo: use narrow only for modules? (correct? efficiency gain?) + def findMember(name: Name, excludedFlags: int, requiredFlags: int): Symbol = { + if (util.Statistics.enabled) findMemberCount = findMemberCount + 1; + val startTime = if (util.Statistics.enabled) System.currentTimeMillis() else 0l; + + //System.out.println("find member " + name.decode + " in " + this + ":" + this.baseClasses);//DEBUG + var members: Scope = null; + var member: Symbol = NoSymbol; + var excluded = excludedFlags | DEFERRED; + var self: Type = null; + var continue = true; + var savedCheckMalformedSwitch = checkMalformedSwitch; + checkMalformedSwitch = false; + while (continue) { + continue = false; + var bcs = baseClasses; + while (!bcs.isEmpty) { + val decls = bcs.head.info.decls; + bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail; + var entry = if (name == nme.ANYNAME) decls.elems else decls lookupEntry name; + while (entry != null) { + val sym = entry.sym; + if (sym.getFlag(requiredFlags) == requiredFlags) { + val excl = sym.getFlag(excluded); + if (excl == 0) { + if (name.isTypeName) { + checkMalformedSwitch = savedCheckMalformedSwitch; + if (util.Statistics.enabled) findMemberMillis = findMemberMillis + System.currentTimeMillis() - startTime; + return sym + } else if (member == NoSymbol) { + member = sym + } else if (members == null) { + if (member.name != sym.name || + member != sym && + (member.owner == sym.owner || { + if (self == null) self = this.narrow; + !self.memberType(member).matches(self.memberType(sym))})) + members = new Scope(List(member, sym)); + } else { + var prevEntry = members lookupEntry sym.name; + while (prevEntry != null && + !(prevEntry.sym == sym + || + prevEntry.sym.owner != sym.owner && + !prevEntry.sym.hasFlag(PRIVATE) && + !sym.hasFlag(PRIVATE) && { + if (self == null) self = this.narrow; + (self.memberType(prevEntry.sym) matches self.memberType(sym))})) + prevEntry = members lookupNextEntry prevEntry; + if (prevEntry == null) { + members enter sym; + } + } + } else if (excl == DEFERRED) { + continue = true; + } + } + entry = if (name == nme.ANYNAME) entry.next else decls lookupNextEntry entry + } // while (entry != null) + // excluded = excluded | LOCAL + } // while (!bcs.isEmpty) + excluded = excludedFlags + } // while (continue) + checkMalformedSwitch = savedCheckMalformedSwitch; + if (util.Statistics.enabled) findMemberMillis = findMemberMillis + System.currentTimeMillis() - startTime; + if (members == null) { + if (util.Statistics.enabled) if (member == NoSymbol) noMemberCount = noMemberCount + 1; + member + } else { + if (util.Statistics.enabled) multMemberCount = multMemberCount + 1; + baseClasses.head.newOverloaded(this, members.toList) + } + } + } + +// Subclasses ------------------------------------------------------------ + + [_trait_] abstract class UniqueType { + private val hashcode = { val h = super.hashCode(); if (h < 0) -h else h } + override def hashCode() = hashcode; + } + + /** A base class for types that defer some operations + * to their immediate supertype + */ + abstract class SubType extends Type { + def supertype: Type; + override def parents: List[Type] = supertype.parents; + override def decls: Scope = supertype.decls; + override def baseType(clazz: Symbol): Type = supertype.baseType(clazz); + override def closure: Array[Type] = supertype.closure; + override def baseClasses: List[Symbol] = supertype.baseClasses; + } + + /** A base class for types that represent a single value + * (single-types and this-types) + */ + abstract class SingletonType extends SubType { + override def singleDeref: Type; + def supertype: Type = singleDeref; + override def isStable: boolean = true; + override def widen: Type = singleDeref.widen; + override def closure: Array[Type] = { + if (util.Statistics.enabled) singletonClosureCount = singletonClosureCount + 1; + addClosure(this, supertype.closure); + } + override def toString(): String = prefixString + "type"; + } + + /** An object representing an erroneous type */ + case object ErrorType extends Type { + // todo see whether we can do without + override def isError: boolean = true; + override def decls: Scope = new ErrorScope(NoSymbol); + override def findMember(name: Name, excludedFlags: int, requiredFlags: int): Symbol = { + var sym = decls lookup name; + if (sym == NoSymbol) { + sym = NoSymbol.newErrorSymbol(name); + decls enter sym + } + sym + } + override def baseType(clazz: Symbol): Type = this; + override def toString(): String = "<error>"; + override def narrow: Type = this; + } + + /** An object representing an unknown type */ + case object WildcardType extends Type { + override def toString(): String = "?" + } + + /** An object representing a non-existing type */ + case object NoType extends Type { + override def isTrivial: boolean = true; + override def toString(): String = "<notype>" + } + + /** An object representing a non-existing prefix */ + case object NoPrefix extends Type { + override def isTrivial: boolean = true; + override def isStable: boolean = true; + override def prefixString = ""; + override def toString(): String = "<noprefix>"; + } + + /** A class for this-types of the form <sym>.this.type + */ + abstract case class ThisType(sym: Symbol) extends SingletonType { + //assert(sym.isClass && !sym.isModuleClass || sym.isRoot, sym); + override def isTrivial: boolean = sym.isPackageClass; + override def symbol = sym; + override def singleDeref: Type = sym.typeOfThis; + override def prefixString = + if (settings.debug.value) sym.nameString + ".this."; + else if (sym.isRoot || sym.isEmptyPackageClass) "" + else if (sym.isAnonymousClass || sym.isRefinementClass) "this." + else if (sym.isPackageClass) sym.fullNameString + "." + else sym.nameString + ".this."; + override def narrow: Type = this; + } + + /** A class for singleton types of the form <prefix>.<sym.name>.type. + * Cannot be created directly; one should always use + * `singleType' for creation. + */ + abstract case class SingleType(pre: Type, sym: Symbol) extends SingletonType { + override val isTrivial: boolean = pre.isTrivial; + private var singleDerefCache: Type = _; + private var singleDerefPhase: Phase = null; + override def singleDeref: Type = { + val p = singleDerefPhase; + if (p != phase) { + singleDerefPhase = phase; + if (!isValid(p)) { + singleDerefCache = pre.memberType(sym).resultType; + } + } + singleDerefCache + } + override def symbol = sym; + override def prefix: Type = pre; + override def prefixString: String = + if (sym.isEmptyPackage && !settings.debug.value) "" + else pre.prefixString + sym.nameString + "."; + } + + abstract case class SuperType(thistpe: Type, supertp: Type) extends SingletonType { + override val isTrivial: boolean = thistpe.isTrivial && supertp.isTrivial; + override def symbol = thistpe.symbol; + override def singleDeref = supertp; + override def prefix: Type = supertp.prefix; + override def prefixString = + if (thistpe.prefixString.endsWith("this.")) + thistpe.prefixString.substring(0, thistpe.prefixString.length() - 5) + "super." + else thistpe.prefixString; + override def narrow: Type = thistpe.narrow + } + + /** A class for the bounds of abstract types and type parameters + */ + abstract case class TypeBounds(lo: Type, hi: Type) extends SubType { + override val isTrivial: boolean = lo.isTrivial && hi.isTrivial; + def supertype: Type = hi; + override def bounds: TypeBounds = this; + def containsType(that: Type) = that <:< this || lo <:< that && that <:< hi; + override def toString() = ">: " + lo + " <: " + hi; + } + + /** A common base class for intersection types and class types + */ + abstract class CompoundType extends Type { + assert(!parents.exists (.isInstanceOf[TypeBounds]), this);//debug + + private var closureCache: Array[Type] = _; + private var closurePhase: Phase = null; + private var baseClassesCache: List[Symbol] = _; + private var baseClassesPhase: Phase = null; + + override def closure: Array[Type] = { + def computeClosure: Array[Type] = + try { + if (util.Statistics.enabled) compoundClosureCount = compoundClosureCount + 1; + //System.out.println("computing closure of " + symbol.tpe + " " + parents);//DEBUG + val buf = new ListBuffer[Type]; + buf += symbol.tpe; + var clSize = 1; + val nparents = parents.length; + if (nparents != 0) { + val pclosure = new Array[Array[Type]](nparents); + val index = new Array[int](nparents); + var i = 0; + for (val p <- parents) { + pclosure(i) = p.closure; + index(i) = 0; + i = i + 1 + } + def nextBaseType(i: int): Type = { + val j = index(i); + val pci = pclosure(i); + if (j < pci.length) pci(j) else AnyClass.tpe + } + val limit = pclosure(0).length; + while (index(0) != limit) { + var minSym: Symbol = nextBaseType(0).symbol; + i = 1; + while (i < nparents) { + if (nextBaseType(i).symbol isLess minSym) minSym = nextBaseType(i).symbol; + i = i + 1 + } + var minTypes: List[Type] = List(); + i = 0; + while (i < nparents) { + val tp = nextBaseType(i); + if (tp.symbol == minSym) { + if (!(minTypes exists (tp =:=))) minTypes = tp :: minTypes; + index(i) = index(i) + 1 + } + i = i + 1 + } + buf += intersectionType(minTypes); + clSize = clSize + 1; + } + } + closureCache = new Array[Type](clSize); + buf.copyToArray(closureCache, 0); + //System.out.println("closureCache of " + symbol.tpe + " = " + List.fromArray(closureCache));//DEBUG + var j = 0; + while (j < clSize) { + closureCache(j) match { + case RefinedType(parents, decls) => + assert(decls.isEmpty); + closureCache(j) = glb(parents) + case _ => + } + j = j + 1 + } + //System.out.println("closure of " + symbol.tpe + " = " + List.fromArray(closureCache));//DEBUG + closureCache + } catch { + case ex: MalformedClosure => + throw new MalformedType( + "the type intersection " + this + " is malformed" + + "\n --- because ---\n" + ex.getMessage()) + } + val p = closurePhase; + if (p != phase) { + closurePhase = phase; + if (!isValidForBaseClasses(p)) { + closureCache = null; + closureCache = computeClosure + } + //System.out.println("closure(" + symbol + ") = " + List.fromArray(closureCache));//DEBUG + } + if (closureCache == null) + throw new TypeError("illegal cyclic reference involving " + symbol); + closureCache; + } + + override def baseClasses: List[Symbol] = { + def computeBaseClasses: List[Symbol] = + if (parents.isEmpty) List(symbol) + else { + //System.out.println("computing base classes of " + symbol + " at phase " + phase);//DEBUG + // optimized, since this seems to be performance critical + val superclazz = parents.head; + var mixins = parents.tail; + val sbcs = superclazz.baseClasses; + var bcs = sbcs; + def isNew(clazz: Symbol): boolean = ( + superclazz.closurePos(clazz) < 0 && + { var p = bcs; + while ((p ne sbcs) && (p.head != clazz)) p = p.tail; + p eq sbcs + } + ); + while (!mixins.isEmpty) { + def addMixinBaseClasses(mbcs: List[Symbol]): List[Symbol] = + if (mbcs.isEmpty) bcs + else if (isNew(mbcs.head)) mbcs.head :: addMixinBaseClasses(mbcs.tail) + else addMixinBaseClasses(mbcs.tail); + bcs = addMixinBaseClasses(mixins.head.baseClasses); + mixins = mixins.tail + } + symbol :: bcs + } + val p = baseClassesPhase; + if (p != phase) { + baseClassesPhase = phase; + if (!isValidForBaseClasses(p)) { + baseClassesCache = null; + baseClassesCache = computeBaseClasses; + } + } + if (baseClassesCache == null) + throw new TypeError("illegal cyclic reference involving " + symbol); + baseClassesCache + } + + override def baseType(sym: Symbol): Type = { + val index = closurePos(sym); + if (index >= 0) closure(index) else NoType; + } + + override def narrow: Type = symbol.thisType; + + override def toString(): String = ( + parents.mkString("", " with ", "") + + (if (settings.debug.value || parents.isEmpty || decls.elems != null) + decls.mkString("{", "; ", "}") else "") + ); + } + + /** A class representing intersection types with refinements of the form + * <parents_0> with ... with <parents_n> { decls } + * Cannot be created directly; + * one should always use `refinedType' for creation. + */ + abstract case class RefinedType(override val parents: List[Type], + override val decls: Scope) extends CompoundType; + + /** A class representing a class info + */ + case class ClassInfoType(override val parents: List[Type], + override val decls: Scope, + override val symbol: Symbol) extends CompoundType; + + class PackageClassInfoType(decls: Scope, clazz: Symbol) extends ClassInfoType(List(), decls, clazz); + + /** A class representing a constant type */ + abstract case class ConstantType(value: Constant) extends SingletonType { + assert(value.tpe.symbol != UnitClass); + override def isTrivial: boolean = true; + override def symbol: Symbol = value.tpe.symbol; + override def singleDeref: Type = value.tpe; + override def deconst: Type = value.tpe; + override def toString(): String = value.tpe.toString() + "(" + value.stringValue + ")"; + } + + /** A class for named types of the form <prefix>.<sym.name>[args] + * Cannot be created directly; one should always use `typeRef' for creation. + */ + abstract case class TypeRef(pre: Type, sym: Symbol, args: List[Type]) extends Type { + assert(!sym.isAbstractType || pre.isStable || pre.isError); + assert(!pre.isInstanceOf[ClassInfoType], this); + assert(!sym.isTypeParameterOrSkolem || pre == NoPrefix, this); + + private var parentsCache: List[Type] = _; + private var parentsPhase: Phase = null; + private var closureCache: Array[Type] = _; + private var closurePhase: Phase = null; + + override val isTrivial: boolean = + pre.isTrivial && !sym.isTypeParameter && args.forall(.isTrivial); + + def transform(tp: Type): Type = + tp.asSeenFrom(pre, sym.owner).subst(sym.typeParams, args); + + def transform(cl: Array[Type]): Array[Type] = { + val cl1 = new Array[Type](cl.length); + var i = 0; + while (i < cl.length) { cl1(i) = transform(cl(i)); i = i + 1 } + cl1 + } + + override def symbol = sym; + + override def bounds: TypeBounds = + if (sym.isAbstractType) transform(sym.info.bounds).asInstanceOf[TypeBounds] + else super.bounds; + + override def parents: List[Type] = { + val p = parentsPhase; + if (p != phase) { + parentsPhase = phase; + if (!isValidForBaseClasses(p)) { + parentsCache = sym.info.parents map transform + } + } + parentsCache + } + + override def typeOfThis = transform(sym.typeOfThis); + + override def narrow = if (sym.isModuleClass) transform(sym.thisType) else super.narrow; + + override def prefix: Type = pre; + + override def typeArgs: List[Type] = args; + + override def typeParams: List[Symbol] = + if (args.isEmpty) symbol.unsafeTypeParams else List(); + + override def decls: Scope = { + sym.info match { + case TypeRef(_, sym1, _) => + assert(sym1 != symbol, this); + case _ => + } + sym.info.decls + } + + override def baseType(clazz: Symbol): Type = + if (sym == clazz) this + else if (sym.isClass) transform(sym.info.baseType(clazz)) + else pre.memberInfo(sym).baseType(clazz); + + override def closure: Array[Type] = { + val p = closurePhase; + if (p != phase) { + closurePhase = phase; + if (!isValidForBaseClasses(p)) { + if (util.Statistics.enabled) typerefClosureCount = typerefClosureCount + 1; + closureCache = + if (sym.isAbstractType) addClosure(this, transform(bounds.hi).closure) + else transform(sym.info.closure); + } + } + closureCache + } + + override def baseClasses: List[Symbol] = sym.info.baseClasses; + + override def toString(): String = { + if (!settings.debug.value) { + if (sym == RepeatedParamClass && !args.isEmpty) + return args(0).toString() + "*"; + if (sym == ByNameParamClass && !args.isEmpty) + return "=> " + args(0).toString(); + if (isFunctionType(this)) + return args.init.mkString("(", ", ", ")") + " => " + args.last; + } + (pre.prefixString + sym.nameString + + (if (args.isEmpty) "" else args.mkString("[", ",", "]"))) + } + + override def prefixString = + if (settings.debug.value) super.prefixString + else if (sym.isRoot || sym.isEmptyPackageClass || + sym.isAnonymousClass || sym.isRefinementClass) "" + else if (sym.isPackageClass) sym.fullNameString + "." + else super.prefixString; + } + + /** A class representing a method type with parameters. + */ + case class MethodType(override val paramTypes: List[Type], + override val resultType: Type) extends Type { + override val isTrivial: boolean = + paramTypes.forall(.isTrivial) && resultType.isTrivial; + + assert(paramTypes forall (pt => !pt.symbol.isImplClass));//debug + override def paramSectionCount: int = resultType.paramSectionCount + 1; + + override def finalResultType: Type = resultType.finalResultType; + + override def toString(): String = paramTypes.mkString("(", ",", ")") + resultType; + } + + class ImplicitMethodType(pts: List[Type], rt: Type) extends MethodType(pts, rt) { + override def toString(): String = paramTypes.mkString("(implicit ", ",", ")") + resultType; + } + + class JavaMethodType(pts: List[Type], rt: Type) extends MethodType(pts, rt); + + /** A class representing a polymorphic type or, if tparams.length == 0, + * a parameterless method type. + */ + case class PolyType(override val typeParams: List[Symbol], override val resultType: Type) + extends Type { + + override def paramSectionCount: int = resultType.paramSectionCount; + override def paramTypes: List[Type] = resultType.paramTypes; + + override def finalResultType: Type = resultType.finalResultType; + + override def parents: List[Type] = resultType.parents; + override def decls: Scope = resultType.decls; + override def symbol: Symbol = resultType.symbol; + override def closure: Array[Type] = resultType.closure; + override def baseClasses: List[Symbol] = resultType.baseClasses; + override def baseType(clazz: Symbol): Type = resultType.baseType(clazz); + override def narrow: Type = resultType.narrow; + + override def toString(): String = + (if (typeParams.isEmpty) "=> " + else (typeParams map (.defString)).mkString("[", ",", "]")) + resultType; + + override def cloneInfo(owner: Symbol) = { + val tparams = cloneSymbols(typeParams, owner); + PolyType(tparams, resultType.substSym(typeParams, tparams)) + } + } + + /** A class containing the alternatives and type prefix of an overloaded symbol. + * Not used after phase `typer'. + */ + case class OverloadedType(pre: Type, alternatives: List[Symbol]) extends Type { + override def prefix: Type = pre; + override def toString() = (alternatives map pre.memberType).mkString("", " <and> ", "") + } + + /** A class remembering a type instantiation for some a set of overloaded polymorphic symbols. + * Not used after phase `typer'. + */ + case class AntiPolyType(pre: Type, targs: List[Type]) extends Type { + override def toString() = pre.toString() + targs.mkString("(with type arguments ", ",", ")"); + override def memberType(sym: Symbol) = pre.memberType(sym) match { + case PolyType(tparams, restp) => restp.subst(tparams, targs) + } + } + + /** A class representing a type variable + * Not used after phase `typer'. + */ + case class TypeVar(origin: Type, constr: TypeConstraint) extends Type { + override def symbol = origin.symbol; + override def toString(): String = + if (constr.inst eq NoType) "?" + origin else constr.inst.toString(); + } + + /** A class representing an as-yet unevaluated type. + */ + abstract class LazyType extends Type { + override def isComplete: boolean = false; + override def complete(sym: Symbol): unit; + } + + /** A class representing a lazy type with known type parameters + */ + class LazyPolyType(override val typeParams: List[Symbol], restp: Type) extends LazyType { + override def complete(sym: Symbol): unit = { + restp.complete(sym); + } + } + +// Creators --------------------------------------------------------------- + + /** Rebind symbol `sym' to an overriding member in type `pre' */ + private def rebind(pre: Type, sym: Symbol): Symbol = { + val owner = sym.owner; + if (owner.isClass && owner != pre.symbol && !sym.isFinal) { + val rebind = pre.nonPrivateMember(sym.name).suchThat(sym => sym.isType || sym.isStable); + if (rebind == NoSymbol) sym else rebind + } else sym + } + + /** The canonical creator for this-types */ + def ThisType(sym: Symbol): Type = + if (phase.erasedTypes) sym.tpe else unique(new ThisType(sym) with UniqueType); + + /** The canonical creator for single-types */ + def singleType(pre: Type, sym: Symbol): Type = { + if (phase.erasedTypes) + sym.tpe.resultType + else if (checkMalformedSwitch && !pre.isStable && !pre.isError) + throw new MalformedType(pre, sym.name.toString()) + else + unique(new SingleType(pre, rebind(pre, sym)) with UniqueType) + } + + /** The canonical creator for super-types */ + def SuperType(thistp: Type, supertp: Type): Type = + if (phase.erasedTypes) supertp + else unique(new SuperType(thistp, supertp) with UniqueType); + + /** The canonical creator for type bounds */ + def TypeBounds(lo: Type, hi: Type): TypeBounds = + unique(new TypeBounds(lo, hi) with UniqueType); + + /** the canonical creator for a refined type with a given scope */ + def refinedType(parents: List[Type], owner: Symbol, decls: Scope): Type = { + if (phase.erasedTypes) + if (parents.isEmpty) ObjectClass.tpe else parents.head + else { + val clazz = owner.newRefinementClass(Position.NOPOS); + val result = new RefinedType(parents, decls) { override def symbol: Symbol = clazz } + clazz.setInfo(result); + result + } + } + + /** the canonical creator for a refined type with an initially empty scope */ + def refinedType(parents: List[Type], owner: Symbol): Type = + refinedType(parents, owner, new Scope); + + /** the canonical creator for a constant type */ + def ConstantType(value: Constant): ConstantType = + unique(new ConstantType(value) with UniqueType); + + /** The canonical creator for typerefs */ + def typeRef(pre: Type, sym: Symbol, args: List[Type]): Type = { + val sym1 = if (sym.isAbstractType) rebind(pre, sym) else sym; + if (checkMalformedSwitch && sym1.isAbstractType && !pre.isStable && !pre.isError) + throw new MalformedType(pre, sym.nameString); +// if (sym1.hasFlag(LOCKED)) +// throw new TypeError("illegal cyclic reference involving " + sym1); + if (sym1.isAliasType && sym1.info.typeParams.length == args.length) { + // note: we require that object is initialized, + // that's why we use info.typeParams instead of typeParams. + if (sym1.hasFlag(LOCKED)) + throw new TypeError("illegal cyclic reference involving " + sym1); + sym1.setFlag(LOCKED); + val result = sym1.info.resultType.asSeenFrom(pre, sym1.owner).subst(sym1.typeParams, args); + sym1.resetFlag(LOCKED); + result + } else { + rawTypeRef(pre, sym1, args) + } + } + + /** create a type-ref as found, without checks or rebinds */ + def rawTypeRef(pre: Type, sym: Symbol, args: List[Type]): Type = { + unique(new TypeRef(pre, sym, args) with UniqueType) + } + + /** The canonical creator for implicit method types */ + def ImplicitMethodType(paramTypes: List[Type], resultType: Type): ImplicitMethodType = + new ImplicitMethodType(paramTypes, resultType); // don't unique this! + + /** The canonical creator for implicit method types */ + def JavaMethodType(paramTypes: List[Type], resultType: Type): JavaMethodType = + new JavaMethodType(paramTypes, resultType); // don't unique this! + + /** A creator for intersection type where intersections of a single type are + * replaced by the type itself. */ + def intersectionType(tps: List[Type], owner: Symbol): Type = tps match { + case List(tp) => tp + case _ => refinedType(tps, owner) + } + + /** A creator for intersection type where intersections of a single type are + * replaced by the type itself. */ + def intersectionType(tps: List[Type]): Type = tps match { + case List(tp) => tp + case _ => refinedType(tps, commonOwner(tps)) + } + + /** A creator for type applications */ + def appliedType(tycon: Type, args: List[Type]): Type = tycon match { + case TypeRef(pre, sym, _) => typeRef(pre, sym, args) + case PolyType(tparams, restpe) => restpe.subst(tparams, args) + case ErrorType => tycon + case _ => + System.out.println(tycon.getClass()); + System.out.println(tycon.$tag()); + throw new Error(); + } + +// Hash consing -------------------------------------------------------------- + + private val uniques = new HashSet[AnyRef](20000); + + def uniqueTypeCount = uniques.size; // for statistics + + private def unique[T <: AnyRef](tp: T): T = { + val tp1 = uniques.findEntry(tp); + if (tp1 == null) { + uniques.addEntry(tp); tp + } else { + tp1.asInstanceOf[T] + } + } + +// Helper Classes --------------------------------------------------------- + + /** A class expressing upper and lower bounds constraints + * for type variables, as well as their instantiations */ + class TypeConstraint { + var lobounds: List[Type] = List(); + var hibounds: List[Type] = List(); + var inst: Type = NoType; + + def instantiate(tp: Type): boolean = + if (lobounds.forall(.<:<(tp)) && hibounds.forall(tp.<:<)) { + inst = tp; true + } else false; + } + + /** A prototype for mapping a function over all possible types + */ + trait TypeMap extends Function1[Type, Type] { + // deferred inherited: def apply(tp: Type): Type + + private def cloneDecls(result: Type, tp: Type, decls: Scope): Type = { + val syms1 = decls.toList; + for (val sym <- syms1) + result.decls.enter(sym.cloneSymbol(result.symbol)); + val syms2 = result.decls.toList; + val resultThis = result.symbol.thisType; + for (val sym <- syms2) + sym.setInfo(sym.info.substSym(syms1, syms2).substThis(tp.symbol, resultThis)); + result + } + + /** Map this function over given type */ + def mapOver(tp: Type): Type = tp match { + case ErrorType => tp + case WildcardType => tp + case NoType => tp + case NoPrefix => tp + case ThisType(_) => tp + case ConstantType(_) => tp + case SingleType(pre, sym) => + if (sym.isPackageClass) tp // short path + else { + val pre1 = this(pre); + if (pre1 eq pre) tp + else singleType(pre1, sym) + } + case SuperType(thistp, supertp) => + val thistp1 = this(thistp); + val supertp1 = this(supertp); + if ((thistp1 eq thistp) && (supertp1 eq supertp)) tp + else SuperType(thistp1, supertp1) + case TypeRef(pre, sym, args) => + val pre1 = this(pre); + val args1 = List.mapConserve(args)(this); + if ((pre1 eq pre) && (args1 eq args)) tp + else typeRef(pre1, sym, args1) + case TypeBounds(lo, hi) => + val lo1 = this(lo); + val hi1 = this(hi); + if ((lo1 eq lo) && (hi1 eq hi)) tp + else TypeBounds(lo1, hi1) + case RefinedType(parents, decls) => + val parents1 = List.mapConserve(parents)(this); + val decls1 = mapOver(decls); + if ((parents1 eq parents) && (decls1 eq decls)) tp + else cloneDecls(refinedType(parents1, tp.symbol.owner), tp, decls1) +/* + case ClassInfoType(parents, decls, clazz) => + val parents1 = List.mapConserve(parents)(this); + val decls1 = mapOver(decls); + if ((parents1 eq parents) && (decls1 eq decls)) tp + else cloneDecls(ClassInfoType(parents1, new Scope(), clazz), tp, decls1) +*/ + case MethodType(paramtypes, result) => + val paramtypes1 = List.mapConserve(paramtypes)(this); + val result1 = this(result); + if ((paramtypes1 eq paramtypes) && (result1 eq result)) tp + else if (tp.isInstanceOf[ImplicitMethodType]) ImplicitMethodType(paramtypes1, result1) + else if (tp.isInstanceOf[JavaMethodType]) JavaMethodType(paramtypes1, result1) + else MethodType(paramtypes1, result1) + case PolyType(tparams, result) => + val tparams1 = mapOver(tparams); + var result1 = this(result); + if ((tparams1 eq tparams) && (result1 eq result)) tp + else PolyType(tparams1, result1.substSym(tparams, tparams1)) + case OverloadedType(pre, alts) => + val pre1 = if (pre.isInstanceOf[ClassInfoType]) pre else this(pre); + if (pre1 eq pre) tp + else OverloadedType(pre1, alts) + case AntiPolyType(pre, args) => + val pre1 = this(pre); + val args1 = List.mapConserve(args)(this); + if ((pre1 eq pre) && (args1 eq args)) tp + else AntiPolyType(pre1, args1) + case TypeVar(_, constr) => + if (constr.inst != NoType) this(constr.inst) + else tp + case _ => + tp + // throw new Error("mapOver inapplicable for " + tp); + } + + /** Map this function over given scope */ + private def mapOver(scope: Scope): Scope = { + val elems = scope.toList; + val elems1 = mapOver(elems); + if (elems1 eq elems) scope + else new Scope(elems1) + } + + /** Map this function over given list of symbols */ + private def mapOver(syms: List[Symbol]): List[Symbol] = { + val infos = syms map (.info); + val infos1 = List.mapConserve(infos)(this); + if (infos1 eq infos) syms + else { + val syms1 = syms map (.cloneSymbol); + (List.map2(syms1, infos1) + ((sym1, info1) => sym1.setInfo(info1.substSym(syms, syms1)))) + } + } + } + + abstract class TypeTraverser extends TypeMap { + def traverse(tp: Type): TypeTraverser; //todo: return unit instead? + def apply(tp: Type): Type = { traverse(tp); tp } + } + + /** A map to compute the asSeenFrom method */ + class AsSeenFromMap(pre: Type, clazz: Symbol) extends TypeMap { + def apply(tp: Type): Type = + if ((pre eq NoType) || (pre eq NoPrefix) || !clazz.isClass) tp + else tp match { + case ThisType(sym) => + def toPrefix(pre: Type, clazz: Symbol): Type = + if ((pre eq NoType) || (pre eq NoPrefix) || !clazz.isClass) tp + else if ((sym isSubClass clazz) && (pre.widen.symbol isSubClass sym)) pre + else toPrefix(pre.baseType(clazz).prefix, clazz.owner); + toPrefix(pre, clazz) + case TypeRef(prefix, sym, args) if (sym.isTypeParameter) => + def toInstance(pre: Type, clazz: Symbol): Type = + if ((pre eq NoType) || (pre eq NoPrefix) || !clazz.isClass) tp + else { + val symclazz = sym.owner; + def throwError = + throw new Error("" + tp + " in " + symclazz + + " cannot be instantiated from " + pre.widen); + def instParam(ps: List[Symbol], as: List[Type]): Type = + if (ps.isEmpty) throwError + else if (sym eq ps.head) as.head + else instParam(ps.tail, as.tail); + if (symclazz == clazz && (pre.widen.symbol isSubClass symclazz)) + pre.baseType(symclazz) match { + case TypeRef(_, basesym, baseargs) => + if (basesym.typeParams.length != baseargs.length) + assert(false, "asSeenFrom(" + pre + "," + clazz + ")" + sym + " " + basesym + " " + baseargs); //debug + instParam(basesym.typeParams, baseargs); + case _ => + throwError + } + else toInstance(pre.baseType(clazz).prefix, clazz.owner) + } + toInstance(pre, clazz) + case _ => + mapOver(tp) + } + } + + /** A base class to compute all substitutions */ + abstract class SubstMap[T](from: List[Symbol], to: List[T]) extends TypeMap { + + /** Are sym1, sym1 the same. Can be tunded by subclasses */ + protected def matches(sym: Symbol, sym1: Symbol): boolean = sym eq sym1; + + /** Map target to type, can be tuned by subclasses */ + protected def toType(fromtp: Type, t: T): Type; + + def apply(tp: Type): Type = { + def subst(sym: Symbol, from: List[Symbol], to: List[T]): Type = + if (from.isEmpty) tp + else if (matches(from.head, sym)) toType(tp, to.head) + else subst(sym, from.tail, to.tail); + tp match { + case TypeRef(NoPrefix, sym, _) => + subst(sym, from, to) + case SingleType(NoPrefix, sym) => + subst(sym, from, to) + case PolyType(tparams, restp) => + assert(!(tparams exists (from contains))); + mapOver(tp) + case _ => + mapOver(tp) + } + } + } + + /** A map to implement the substSym method */ + class SubstSymMap(from: List[Symbol], to: List[Symbol]) + extends SubstMap(from, to) { + protected def toType(fromtp: Type, sym: Symbol) = fromtp match { + case TypeRef(pre, _, args) => typeRef(pre, sym, args) + case SingleType(pre, _) => singleType(pre, sym) + } + } + + /** A map to implement the subst method */ + class SubstTypeMap(from: List[Symbol], to: List[Type]) + extends SubstMap(from, to) { + protected def toType(fromtp: Type, tp: Type) = tp; + } + + /** A map to implement the substThis method */ + class SubstThisMap(from: Symbol, to: Type) extends TypeMap { + def apply(tp: Type): Type = tp match { + case ThisType(sym) if (sym == from) => to + case _ => mapOver(tp) + } + } + + class SubstSuperMap(from: Type, to: Type) extends TypeMap { + def apply(tp: Type): Type = if (tp eq from) to else mapOver(tp); + } + + /** A map to convert every occurrence of a wildcard type to a fresh + * type variable */ + object wildcardToTypeVarMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case WildcardType => TypeVar(tp, new TypeConstraint) + case _ => mapOver(tp) + } + } + + /** A map to implement the contains method */ + class ContainsTraverser(sym: Symbol) extends TypeTraverser { + var result = false; + def traverse(tp: Type): ContainsTraverser = { + if (!result) { + tp match { + case TypeRef(_, sym1, _) if (sym == sym1) => result = true + case SingleType(_, sym1) if (sym == sym1) => result = true + case _ => mapOver(tp) + } + } + this + } + } + + /** A map to compute the most deeply nested owner that contains all the symbols + * of thistype or prefixless typerefs/singletype occurrences in given type */ + object commonOwnerMap extends TypeMap { + var result: Symbol = _; + def init = { result = NoSymbol } + def apply(tp: Type): Type = { + tp match { + case ThisType(sym) => + register(sym); + case TypeRef(NoPrefix, sym, args) => + register(sym.owner); args foreach {arg => apply(arg); ()} + case SingleType(NoPrefix, sym) => + register(sym.owner); + case _ => + mapOver(tp) + } + tp + } + private def register(sym: Symbol): unit = { + while (result != NoSymbol && sym != result && !(sym isNestedIn result)) + result = result.owner; + } + } + + object adaptToNewRunMap extends TypeMap { + private def adaptToNewRun(pre: Type, sym: Symbol): Symbol = { + if (sym.isModuleClass) adaptToNewRun(pre, sym.sourceModule).moduleClass; + else if ((pre eq NoPrefix) || (pre eq NoType) || sym.owner.isPackageClass) sym + else { + val rebind0 = pre.member(sym.name); + val rebind = rebind0.suchThat(sym => sym.isType || sym.isStable); + if (rebind == NoSymbol) throw new MalformedType(pre, sym.name.toString()); + rebind + } + } + def apply(tp: Type): Type = tp match { + case ThisType(sym) if (sym.isModuleClass) => + val sym1 = adaptToNewRun(sym.owner.thisType, sym); + if (sym1 == sym) tp else ThisType(sym1) + case SingleType(pre, sym) => + if (sym.isPackage) tp + else { + val pre1 = this(pre); + val sym1 = adaptToNewRun(pre1, sym); + if ((pre1 eq pre) && (sym1 eq sym)) tp + else singleType(pre1, sym1) + } + case TypeRef(pre, sym, args) => + if (sym.isPackageClass) tp + else { + val pre1 = this(pre); + val args1 = List.mapConserve(args)(this); + val sym1 = adaptToNewRun(pre1, sym); + if ((pre1 eq pre) && (sym1 eq sym) && (args1 eq args) && sym.isExternal) tp + else typeRef(pre1, sym1, args1) + } + case PolyType(tparams, restp) => + val restp1 = this(restp); + if (restp1 eq restp) tp + else PolyType(tparams, restp1) + case ClassInfoType(parents, decls, clazz) => + val parents1 = List.mapConserve(parents)(this); + if (parents1 eq parents) tp + else ClassInfoType(parents1, decls, clazz) + case RefinedType(parents, decls) => + val parents1 = List.mapConserve(parents)(this); + if (parents1 eq parents) tp + else refinedType(parents1, tp.symbol.owner, decls) + case SuperType(_, _) => mapOver(tp) + case TypeBounds(_, _) => mapOver(tp) + case MethodType(_, _) => mapOver(tp) + case TypeVar(_, _) => mapOver(tp) + case _ => tp + } + } + + object freeTypeParams extends TypeTraverser { + private var result: List[Symbol] = _; + private def includeIfAbstract(sym: Symbol): unit = { + if (sym.isAbstractType && !result.contains(sym)) result = sym :: result; + } + override def traverse(tp: Type): TypeTraverser = { + tp match { + case TypeRef(NoPrefix, sym, _) => + includeIfAbstract(sym) + case TypeRef(ThisType(_), sym, _) => + includeIfAbstract(sym) + case _ => + } + mapOver(tp); + this + } + def collect(tp: Type): List[Symbol] = { + result = List(); + traverse(tp); + result + } + } + +// Helper Methods ------------------------------------------------------------- + + final def isValid(p: Phase): boolean = + p != null && phaseWithId(p.id) == p && { + if (phase.id > p.id) infoTransformers.nextFrom(p.id).pid >= phase.id + else infoTransformers.nextFrom(phase.id).pid >= p.id + } + + final def isValidForBaseClasses(p: Phase): boolean = { + def noChangeInBaseClasses(it: InfoTransformer, limit: Phase#Id): boolean = ( + it.pid >= limit || + !it.changesBaseClasses && noChangeInBaseClasses(it.next, limit) + ); + p != null && phaseWithId(p.id) == p && { + if (phase.id > p.id) noChangeInBaseClasses(infoTransformers.nextFrom(p.id), phase.id) + else noChangeInBaseClasses(infoTransformers.nextFrom(phase.id), p.id) + } + } + + /** Do tp1 and tp2 denote equivalent types? */ + def isSameType(tp1: Type, tp2: Type): boolean = { + Pair(tp1, tp2) match { + case Pair(ErrorType, _) => true + case Pair(WildcardType, _) => true + case Pair(_, ErrorType) => true + case Pair(_, WildcardType) => true + + case Pair(NoType, _) => false + case Pair(NoPrefix, _) => tp2.symbol.isPackageClass + case Pair(_, NoType) => false + case Pair(_, NoPrefix) => tp1.symbol.isPackageClass + + case Pair(ThisType(sym1), ThisType(sym2)) => + sym1 == sym2 + case Pair(SingleType(pre1, sym1), SingleType(pre2, sym2)) + if ((sym1 == sym2) && (pre1 =:= pre2)) => + true + case Pair(SingleType(pre1, sym1), ThisType(sym2)) + if (sym1.isModule && + sym1.moduleClass == sym2 && + pre1 =:= sym2.owner.thisType) => + true + case Pair(ThisType(sym1), SingleType(pre2, sym2)) + if (sym2.isModule && + sym2.moduleClass == sym1 && + pre2 =:= sym1.owner.thisType) => + true + case Pair(ConstantType(value1), ConstantType(value2)) => + value1 == value2 + case Pair(TypeRef(pre1, sym1, args1), TypeRef(pre2, sym2, args2)) => + sym1 == sym2 && (phase.erasedTypes || pre1 =:= pre2) && isSameTypes(args1, args2) + case Pair(RefinedType(parents1, ref1), RefinedType(parents2, ref2)) => + def isSubScope(s1: Scope, s2: Scope): boolean = s2.toList.forall { + sym2 => + val sym1 = s1.lookup(sym2.name); + sym1.info =:= sym2.info.substThis(sym2.owner, sym1.owner.thisType) + } + isSameTypes(parents1, parents2) && isSubScope(ref1, ref2) && isSubScope(ref2, ref1) + case Pair(MethodType(pts1, res1), MethodType(pts2, res2)) => + (pts1.length == pts2.length && + isSameTypes(pts1, pts2) && + res1 =:= res2 && + tp1.isInstanceOf[ImplicitMethodType] == tp2.isInstanceOf[ImplicitMethodType]) + case Pair(PolyType(tparams1, res1), PolyType(tparams2, res2)) => + (tparams1.length == tparams2.length && + List.forall2(tparams1, tparams2) + ((p1, p2) => p1.info =:= p2.info.substSym(tparams2, tparams1)) && + res1 =:= res2.substSym(tparams2, tparams1)) + case Pair(TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => + lo1 =:= lo2 && hi1 =:= hi2 + case Pair(TypeVar(_, constr1), _) => + if (constr1.inst != NoType) constr1.inst =:= tp2 + else constr1 instantiate (wildcardToTypeVarMap(tp2)) + case Pair(_, TypeVar(_, constr2)) => + if (constr2.inst != NoType) tp1 =:= constr2.inst + else constr2 instantiate (wildcardToTypeVarMap(tp1)) + case Pair(SingleType(_, _), _) + if (tp2.isStable && tp1.singleDeref =:= tp2) => + true + case Pair(_, SingleType(_, _)) + if (tp1.isStable && tp1 =:= tp2.singleDeref) => + true + case _ => + false + } + } + + /** Are tps1 and tps2 lists of pairwise equivalent types? */ + def isSameTypes(tps1: List[Type], tps2: List[Type]): boolean = ( + tps1.length == tps2.length && + List.forall2(tps1, tps2)((tp1, tp2) => tp1 =:= tp2) + ); + + var subtypecount = 0; + def isSubType(tp1: Type, tp2: Type): boolean = { + subtypecount = subtypecount + 1; + if (subtypecount == 20) throw new Error("recursive <:<"); + val result = isSubType0(tp1, tp2); + subtypecount = subtypecount - 1; + result + } + + /** Does tp1 conform to tp2? */ + def isSubType0(tp1: Type, tp2: Type): boolean = { + Pair(tp1, tp2) match { + case Pair(ErrorType, _) => true + case Pair(WildcardType, _) => true + case Pair(_, ErrorType) => true + case Pair(_, WildcardType) => true + + case Pair(NoType, _) => false + case Pair(NoPrefix, _) => tp2.symbol.isPackageClass + case Pair(_, NoType) => false + case Pair(_, NoPrefix) => tp1.symbol.isPackageClass + + case Pair(ThisType(_), ThisType(_)) => tp1 =:= tp2 + case Pair(ThisType(_), SingleType(_, _)) => tp1 =:= tp2 + case Pair(SingleType(_, _), ThisType(_)) => tp1 =:= tp2 + case Pair(SingleType(_, _), SingleType(_, _)) => tp1 =:= tp2 + case Pair(ConstantType(_), ConstantType(_)) => tp1 =:= tp2 + + case Pair(TypeRef(pre1, sym1, args1), TypeRef(pre2, sym2, args2)) => + //System.out.println("isSubType " + tp1 + " " + tp2);//DEBUG + def isSubArgs(tps1: List[Type], tps2: List[Type], + tparams: List[Symbol]): boolean = ( + tps1.isEmpty && tps2.isEmpty + || + !tps1.isEmpty && !tps2.isEmpty && + (tparams.head.hasFlag(COVARIANT) || (tps2.head <:< tps1.head)) && + (tparams.head.hasFlag(CONTRAVARIANT) || tps1.head <:< tps2.head) && + isSubArgs(tps1.tail, tps2.tail, tparams.tail) + ); + (sym1 == sym2 && (pre1 <:< pre2) && isSubArgs(args1, args2, sym1.typeParams) + || + sym1.isAbstractType && !(tp1 =:= tp1.bounds.hi) && (tp1.bounds.hi <:< tp2) + || + sym2.isAbstractType && !(tp2 =:= tp2.bounds.lo) && (tp1 <:< tp2.bounds.lo) + || + sym2.isClass && + ({ val base = tp1 baseType sym2; !(base eq tp1) && (base <:< tp2) }) + || + sym1 == AllClass + || + sym1 == AllRefClass && sym2 != AllClass && tp2 <:< AnyRefClass.tpe) + case Pair(MethodType(pts1, res1), MethodType(pts2, res2)) => + (pts1.length == pts2.length && + matchingParams(pts1, pts2, tp2.isInstanceOf[JavaMethodType]) && + (res1 <:< res2) && + tp1.isInstanceOf[ImplicitMethodType] == tp2.isInstanceOf[ImplicitMethodType]) + case Pair(PolyType(tparams1, res1), PolyType(tparams2, res2)) => + (tparams1.length == tparams2.length && + List.forall2(tparams1, tparams2) + ((p1, p2) => p2.info.substSym(tparams2, tparams1) <:< p1.info) && + res1 <:< res2.substSym(tparams2, tparams1)) + case Pair(TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => + lo2 <:< lo1 && hi1 <:< hi2 + case Pair(_, TypeVar(_, constr2)) => + if (constr2.inst != NoType) tp1 <:< constr2.inst + else { constr2.lobounds = tp1 :: constr2.lobounds; true } + case Pair(TypeVar(_, constr1), _) => + if (constr1.inst != NoType) constr1.inst <:< tp2 + else { constr1.hibounds = tp2 :: constr1.hibounds; true } + case Pair(_, RefinedType(parents2, ref2)) => + (parents2 forall tp1.<:<) && (ref2.toList forall tp1.specializes) + case Pair(RefinedType(parents1, ref1), _) => + parents1 exists (.<:<(tp2)) + /* todo: replace following with + case Pair(ThisType(_), _) + | Pair(SingleType(_, _), _) + | Pair(ConstantType(_), _) => + once patern matching bug is fixed */ + case Pair(ThisType(_), _) => tp1.singleDeref <:< tp2 + case Pair(SingleType(_, _), _) => tp1.singleDeref <:< tp2 + case Pair(ConstantType(_), _) => tp1.singleDeref <:< tp2 + + case Pair(TypeRef(pre1, sym1, args1), _) => + (sym1 == AllClass && tp2 <:< AnyClass.tpe + || + sym1 == AllRefClass && tp2.symbol != AllClass && tp2 <:< AnyRefClass.tpe) + case _ => + false + } + } + + /** Are tps1 and tps2 lists of equal length such that all elements + * of tps1 conform to corresponding elements of tps2? */ + def isSubTypes(tps1: List[Type], tps2: List[Type]): boolean = ( + tps1.length == tps2.length && + List.forall2(tps1, tps2)((tp1, tp2) => tp1 <:< tp2) + ); + + /** Does type `tp' implement symbol `sym' with same or stronger type? + * Exact only if `sym' is a member of some refinement type, otherwise + * we might return false negatives */ + def specializesSym(tp: Type, sym: Symbol): boolean = ( + tp.symbol == AllClass || + tp.symbol == AllRefClass && (sym.owner isSubClass ObjectClass) || + (tp.nonPrivateMember(sym.name).alternatives exists + (alt => sym == alt || specializesSym(tp.narrow, alt, sym.owner.thisType, sym))) + ); + + /** Does member `sym1' of `tp1' have a stronger type than member `sym2' of `tp2'? */ + private def specializesSym(tp1: Type, sym1: Symbol, tp2: Type, sym2: Symbol): boolean = { + val info1 = tp1.memberInfo(sym1); + val info2 = tp2.memberInfo(sym2).substThis(tp2.symbol, tp1); + (sym2.isTerm && + info1 <:< info2 || + sym2.isAbstractType && + (info2.bounds containsType info1) || + sym2.isAliasType && + tp2.memberType(sym2) =:= tp1.memberType(sym1)) + } + + /** A function implementing tp1 matches tp2 */ + private def matchesType(tp1: Type, tp2: Type): boolean = Pair(tp1, tp2) match { + case Pair(MethodType(pts1, res1), MethodType(pts2, res2)) => + (matchingParams(pts1, pts2, tp2.isInstanceOf[JavaMethodType]) && (res1 matches res2) && + tp1.isInstanceOf[ImplicitMethodType] == tp2.isInstanceOf[ImplicitMethodType]) + case Pair(PolyType(tparams1, res1), PolyType(tparams2, res2)) => + (tparams1.length == tparams2.length && + (res1 matches res2.substSym(tparams2, tparams1))) + case Pair(PolyType(List(), rtp1), _) => matchesType(rtp1, tp2) + case Pair(_, PolyType(List(), rtp2)) => matchesType(tp1, rtp2) + case Pair(MethodType(_, _), _) => false + case Pair(PolyType(_, _), _) => false + case Pair(_, MethodType(_, _)) => false + case Pair(_, PolyType(_, _)) => false + case _ => + !phase.erasedTypes || tp1 =:= tp2 + } + + /** Are tps1 and tps2 lists of pairwise equivalent types? */ + private def matchingParams(tps1: List[Type], tps2: List[Type], tps2isJava: boolean): boolean = ( + tps1.length == tps2.length && + List.forall2(tps1, tps2)((tp1, tp2) => + (tp1 =:= tp2) || tps2isJava & tp1.symbol == ObjectClass && tp2.symbol == AnyClass) + ); + + /** Prepend type `tp' to closure `cl' */ + private def addClosure(tp: Type, cl: Array[Type]): Array[Type] = { + val cl1 = new Array[Type](cl.length + 1); + assert(!tp.isInstanceOf[CompoundType], tp);//debug + cl1(0) = tp; + System.arraycopy(cl, 0, cl1, 1, cl.length); + cl1 + } + +// Lubs and Glbs --------------------------------------------------------- + + private val recLimit = 10; + private var recCount = 0; + private var giveUp: boolean = _; + + /** Return op(tps), but give up if level of recursion is greater than + * recLimit */ + private def limitRecursion(tps: List[Type], boundkind: String, + op: List[Type] => Type): Type = + if (recCount == recLimit) { + giveUp = true; + AnyClass.tpe + } else { + if (recCount == 0) giveUp = false; + val result = try { + recCount = recCount + 1; + op(tps) + } finally { + recCount = recCount - 1 + } + if (recCount == 0 && giveUp) { + throw new TypeError("failure to compute " + boundkind + + " bound of types " + + tps.mkString("", " and ", ";\n") + + "an approximation is: " + result + ";\n" + + "additional type annotations are needed"); + } + result + } + + /** The greatest sorted upwards closed lower bound of a list of lists of + * types relative to the following ordering <= between lists of types: + * + * xs <= ys iff forall y in ys exists x in xs such that x <: y + * + * @See closure for a definition of sorted and upwards closed. + */ + private def glbList(tss: List[List[Type]]): List[Type] = { + val tss1 = tss filter (ts => !ts.isEmpty); + if (tss1.isEmpty) List() + else if (tss1.tail.isEmpty) tss.head + else { + val ts0 = tss1 map (.head); + val sym = minSym(ts0); + val ts1 = elimSuper(ts0 filter (.symbol.==(sym))); + mergePrefixAndArgs(ts1, -1) match { + case Some(tp0) => + tp0 :: glbList(tss1 map (ts => if (ts.head.symbol == sym) ts.tail else ts)) + case None => + throw new MalformedClosure(ts1) + } + } + } + + /** The greatest sorted upwards closed lower bound of a list of closures. + * @See glbList for more explanations. + */ + private def glbArray(tss: List[Array[Type]]): Array[Type] = { + val tss1 = tss map (ts: Array[Type] => List.fromArray(ts)); + val glbs = glbList(tss1); + val result = new Array[Type](glbs.length); + var i = 0; + for (val x <- glbs.elements) { result(i) = x; i = i + 1; } + result; + // Predef.Array(glbs: _*); + } + + /** The least sorted upwards closed upper bound of a non-empty list + * of lists of types. + * @See glbList for more explanations. */ + private def lubList(tss: List[List[Type]]): List[Type] = + if (tss.tail.isEmpty) tss.head + else if (tss exists (.isEmpty)) List() + else { + val ts0 = tss map (.head); + val sym = minSym(ts0); + if (ts0 forall (t => t.symbol == sym)) + mergePrefixAndArgs(elimSub(ts0), 1).toList ::: lubList(tss map (.tail)) + else + lubList(tss map (ts => if (ts.head.symbol == sym) ts.tail else ts)) + } + + /** The least sorted upwards closed upper bound of a non-empty list + * of closures. + * @See lubList for more explanations. */ + private def lubArray(tss: List[Array[Type]]): Array[Type] = { + var lubs = lubList(tss map (ts: Array[Type] => List.fromArray(ts))); + var arr = new Array[Type](lubs.length); + var i = 0; + while (i < arr.length) { + arr(i) = lubs.head; + i = i + 1; + lubs = lubs.tail + } + arr + // todo: replace by Predef.Array(lubs: _* ) + } + + /** The minimal symbol (wrt Symbol.isLess) of a list of types */ + private def minSym(tps: List[Type]): Symbol = + (tps.head.symbol /: tps.tail) { + (sym1, tp2) => if (tp2.symbol isLess sym1) tp2.symbol else sym1 + } + + /** A minimal type list which has a given array of types as its closure */ + def spanningTypes(ts: List[Type]): List[Type] = ts match { + case List() => List() + case first :: rest => + first :: spanningTypes( + rest filter (t => !first.symbol.isSubClass(t.symbol))) + } + + /** Eliminate from list of types all elements which are a supertype + * of some other element of the list. */ + private def elimSuper(ts: List[Type]): List[Type] = ts match { + case List() => List() + case t :: ts1 => + val rest = ts1 filter (t1 => !(t <:< t1)); + if (rest exists (t1 => t1 <:< t)) rest else t :: rest + } + + /** Eliminate from list of types all elements which are a subtype + * of some other element of the list. */ + private def elimSub(ts: List[Type]): List[Type] = ts match { + case List() => List() + case t :: ts1 => + val rest = ts1 filter (t1 => !(t1 <:< t)); + if (rest exists (t1 => t <:< t1)) rest else t :: rest + } + + /** The least upper bound wrt <:< of a list of types */ + def lub(ts: List[Type]): Type = { + def lub0(ts0: List[Type]): Type = elimSub(ts0 map (.deconst)) match { + case List() => AllClass.tpe + case List(t) => t + case ts @ PolyType(tparams, _) :: _ => + PolyType( + List.map2(tparams, List.transpose(matchingBounds(ts, tparams))) + ((tparam, bounds) => tparam.cloneSymbol.setInfo(glb(bounds))), + lub0(matchingInstTypes(ts, tparams))) + case ts @ MethodType(pts, _) :: rest => + MethodType(pts, lub0(matchingRestypes(ts, pts))) + case ts @ TypeBounds(_, _) :: rest => + TypeBounds(glb(ts map (.bounds.lo)), lub(ts map (.bounds.hi))) + case ts => + val closures: List[Array[Type]] = ts map (.closure); + val lubBaseTypes: Array[Type] = lubArray(closures); + //log("closures = " + (closures map (cl => List.fromArray(cl))) + ", lubbases = " + List.fromArray(lubBaseTypes));//DEBUG + val lubParents = spanningTypes(List.fromArray(lubBaseTypes)); + val lubOwner = commonOwner(ts); + val lubBase = intersectionType(lubParents, lubOwner); + if (phase.erasedTypes) lubBase + else { + val lubType = refinedType(lubParents, lubOwner); + val lubThisType = lubType.symbol.thisType; + val narrowts = ts map (.narrow); + def lubsym(proto: Symbol): Symbol = { + val prototp = lubThisType.memberInfo(proto); + val syms = narrowts map (t => + t.nonPrivateMember(proto.name).suchThat(sym => + sym.tpe matches prototp.substThis(lubThisType.symbol, t))); + if (syms contains NoSymbol) NoSymbol + else { + val symtypes = + (List.map2(narrowts, syms) + ((t, sym) => t.memberInfo(sym).substThis(t.symbol, lubThisType))); + if (settings.debug.value) log("common symbols: " + syms + ":" + symtypes);//debug + if (proto.isTerm) + proto.cloneSymbol.setInfo(lub(symtypes)) + else if (symtypes.tail forall (symtypes.head =:=)) + proto.cloneSymbol.setInfo(symtypes.head) + else { + def lubBounds(bnds: List[TypeBounds]): TypeBounds = + TypeBounds(glb(bnds map (.lo)), lub(bnds map (.hi))); + proto.owner.newAbstractType(proto.pos, proto.name) + .setInfo(lubBounds(symtypes map (.bounds))) + } + } + } + def refines(tp: Type, sym: Symbol): boolean = { + val syms = tp.nonPrivateMember(sym.name).alternatives; + !syms.isEmpty && (syms forall (alt => + // todo alt != sym is strictly speaking not correct, but without it we lose + // efficiency. + alt != sym && !specializesSym(lubThisType, sym, tp, alt))) + } + for (val sym <- lubBase.nonPrivateMembers) + // add a refinement symbol for all non-class members of lubBase + // which are refined by every type in ts. + if (!sym.isClass && !sym.isConstructor && (narrowts forall (t => refines(t, sym)))) + addMember(lubThisType, lubType, lubsym(sym)); + if (lubType.decls.isEmpty) lubBase else lubType; + } + } + + if (settings.debug.value) { + log(indent + "lub of " + ts);//debug + indent = indent + " "; + } + val res = limitRecursion(ts, "least upper", lub0); + if (settings.debug.value) { + indent = indent.substring(0, indent.length() - 2); + log(indent + "lub of " + ts + " is " + res);//debug + } + res + } + + /** The greatest lower bound wrt <:< of a list of types */ + def glb(ts: List[Type]): Type = { + def glb0(ts0: List[Type]): Type = elimSuper(ts0 map (.deconst)) match { + case List() => AnyClass.tpe + case List(t) => t + case ts @ PolyType(tparams, _) :: _ => + PolyType( + List.map2(tparams, List.transpose(matchingBounds(ts, tparams))) + ((tparam, bounds) => tparam.cloneSymbol.setInfo(lub(bounds))), + glb0(matchingInstTypes(ts, tparams))) + case ts @ MethodType(pts, _) :: rest => + MethodType(pts, glb0(matchingRestypes(ts, pts))) + case ts @ TypeBounds(_, _) :: rest => + TypeBounds(lub(ts map (.bounds.lo)), glb(ts map (.bounds.hi))) + case ts => + try { + val glbOwner = commonOwner(ts); + val glbBase = intersectionType(ts, glbOwner); + if (phase.erasedTypes) glbBase + else { + val glbType = refinedType(ts, glbOwner); + val glbThisType = glbType.symbol.thisType; + def glbsym(proto: Symbol): Symbol = { + val prototp = glbThisType.memberInfo(proto); + val syms = for ( + val t <- ts; + val alt <- t.nonPrivateMember(proto.name).alternatives; + glbThisType.memberInfo(alt) matches prototp) yield alt; + val symtypes = syms map glbThisType.memberInfo; + assert(!symtypes.isEmpty); + proto.cloneSymbol.setInfo( + if (proto.isTerm) glb(symtypes) + else { + def isTypeBound(tp: Type) = tp match { + case TypeBounds(_, _) => true + case _ => false + } + def glbBounds(bnds: List[Type]): TypeBounds = { + val lo = lub(bnds map (.bounds.lo)); + val hi = glb(bnds map (.bounds.hi)); + if (lo <:< hi) TypeBounds(lo, hi) + else throw new MalformedClosure(bnds) + } + val symbounds = symtypes filter isTypeBound; + var result: Type = + if (symbounds.isEmpty) + TypeBounds(AllClass.tpe, AnyClass.tpe) + else glbBounds(symbounds); + for (val t <- symtypes; !isTypeBound(t)) + if (result.bounds containsType t) result = t + else throw new MalformedClosure(symtypes); + result + }) + } + for (val t <- ts; val sym <- t.nonPrivateMembers) + if (!sym.isClass && !sym.isConstructor && !(glbThisType specializes sym)) + addMember(glbThisType, glbType, glbsym(sym)); + if (glbType.decls.isEmpty) glbBase else glbType + } + } catch { + case _: MalformedClosure => + if (ts forall (t => t <:< AnyRefClass.tpe)) AllRefClass.tpe + else AllClass.tpe + } + } + if (settings.debug.value) { + log(indent + "glb of " + ts);//debug + indent = indent + " "; + } + val res = limitRecursion(ts, "greatest lower", glb0); + if (settings.debug.value) { + indent = indent.substring(0, indent.length() - 2); + log(indent + "glb of " + ts + " is " + res);//debug + } + res + } + + /** The most deeply nested owner that contains all the symbols + * of thistype or prefixless typerefs/singletype occurrences in given type */ + private def commonOwner(t: Type): Symbol = { + commonOwnerMap.init; + commonOwnerMap.apply(t); + commonOwnerMap.result + } + + /** The most deeply nested owner that contains all the symbols + * of thistype or prefixless typerefs/singletype occurrences in given list of types */ + private def commonOwner(tps: List[Type]): Symbol = { + if (settings.debug.value) log("computing common owner of types " + tps);//debug + commonOwnerMap.init; + tps foreach { tp => commonOwnerMap.apply(tp); () } + commonOwnerMap.result + } + + /** Compute lub (if variance == 1) or glb (if variance == 0) of given list of types + * `tps'. All types in `tps' are typerefs or singletypes with the same symbol. + * Return Some(x) if the computation succeeds with result `x'. + * Return None if the computuation fails. + */ + private def mergePrefixAndArgs(tps: List[Type], variance: int): Option[Type] = tps match { + case List(tp) => + Some(tp) + case TypeRef(_, sym, _) :: rest => + val pres = tps map (.prefix); + val pre = if (variance == 1) lub(pres) else glb(pres); + val argss = tps map (.typeArgs); + val args = + (List.map2(sym.typeParams, List.transpose(argss)) + ((tparam, as) => + if (tparam.variance == variance) lub(as) + else if (tparam.variance == -variance) glb(as) + else NoType)); + try { + if (args contains NoType) None + else Some(typeRef(pre, sym, args)) + } catch { + case ex: MalformedType => None + } + case SingleType(_, sym) :: rest => + val pres = tps map (.prefix); + val pre = if (variance == 1) lub(pres) else glb(pres); + try { + Some(singleType(pre, sym)) + } catch { + case ex: MalformedType => None + } + } + + /** Make symbol `sym' a member of scope `tp.decls' where `thistp' is the narrowed + * owner type of the scope */ + private def addMember(thistp: Type, tp: Type, sym: Symbol): unit = { + if (settings.debug.value) log("add member " + sym);//debug + if (!(thistp specializes sym)) { + if (sym.isTerm) + for (val alt <- tp.nonPrivateDecl(sym.name).alternatives) + if (specializesSym(thistp, sym, thistp, alt)) + tp.decls unlink alt; + tp.decls enter sym + } + } + + /** All types in list must be polytypes with type parameter lists of + * same length as tparams. + * Returns list of list of bounds infos, where corresponding type + * parameters are renamed to tparams. + */ + private def matchingBounds(tps: List[Type], tparams: List[Symbol]): List[List[Type]] = + tps map { + case PolyType(tparams1, _) if (tparams1.length == tparams.length) => + tparams1 map (tparam => tparam.info.substSym(tparams1, tparams)) + case _ => + throw new Error("lub/glb of incompatible types: " + tps.mkString("", " and ", "")) + } + + /** All types in list must be polytypes with type parameter lists of + * same length as tparams. + * Returns list of instance types, where corresponding type + * parameters are renamed to tparams. + */ + private def matchingInstTypes(tps: List[Type], tparams: List[Symbol]): List[Type] = + tps map { + case PolyType(tparams1, restpe) if (tparams1.length == tparams.length) => + restpe.substSym(tparams1, tparams) + case _ => + throw new Error("lub/glb of incompatible types: " + tps.mkString("", " and ", "")) + } + + /** All types in list must be method types with equal parameter types. + * Returns list of their result types. + */ + private def matchingRestypes(tps: List[Type], pts: List[Type]): List[Type] = + tps map { + case MethodType(pts1, res) if (isSameTypes(pts1, pts)) => + res + case _ => + throw new Error("lub/glb of incompatible types: " + tps.mkString("", " and ", "")) + } + +// Errors and Diagnostics --------------------------------------------------------- + + /** An exception signalling a type error */ + class TypeError(val msg: String) extends java.lang.Error(msg); + + /** An exception signalling a malformed type */ + class MalformedType(msg: String) extends TypeError(msg) { + def this(pre: Type, tp: String) = this("malformed type: " + pre + "#" + tp); + } + + /** An exception signalling a malformed closure */ + class MalformedClosure(ts: List[Type]) + extends TypeError("no common type instance of base types " + + ts.mkString("", " and ", "") + " exists"); + + /** An exception signalling a variance annotation/usage conflict */ + class VarianceError(msg: String) extends TypeError(msg); + + /** The current indentation string for traces */ + private var indent: String = ""; + + /** Perform operation `p' on arguments `tp1', `arg2' and print trace of computation */ + private def explain[T](op: String, p: (Type, T) => boolean, tp1: Type, arg2: T): boolean = { + System.out.println(indent + tp1 + " " + op + " " + arg2 + "?"); + indent = indent + " "; + val result = p(tp1, arg2); + indent = indent.substring(0, indent.length() - 2); + System.out.println(indent + result); + result + } + + /** If option `explaintypes' is set, print a subtype trace for `found' <: `required' */ + def explainTypes(found: Type, required: Type): unit = + if (settings.explaintypes.value) { + val s = explainSwitch; + explainSwitch = true; + found <:< required; + explainSwitch = s + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileConstants.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileConstants.scala new file mode 100644 index 0000000000..f023552443 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileConstants.scala @@ -0,0 +1,42 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab.classfile; + +object ClassfileConstants { + + final val JAVA_MAGIC = 0xCAFEBABE; + final val JAVA_MAJOR_VERSION = 45; + final val JAVA_MINOR_VERSION = 3; + + final val JAVA_ACC_PUBLIC = 0x0001; + final val JAVA_ACC_PRIVATE = 0x0002; + final val JAVA_ACC_PROTECTED = 0x0004; + final val JAVA_ACC_STATIC = 0x0008; + final val JAVA_ACC_FINAL = 0x0010; + final val JAVA_ACC_SUPER = 0x0020; + final val JAVA_ACC_SYNCHRONIZED = 0x0020; + final val JAVA_ACC_VOLATILE = 0x0040; + final val JAVA_ACC_BRIDGE = 0x0040; + final val JAVA_ACC_TRANSIENT = 0x0080; + final val JAVA_ACC_NATIVE = 0x0100; + final val JAVA_ACC_INTERFACE = 0x0200; + final val JAVA_ACC_ABSTRACT = 0x0400; + final val JAVA_ACC_STRICT = 0x0800; + final val JAVA_ACC_SYNTHETIC = 0x1000; + + 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; +} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala new file mode 100644 index 0000000000..0e56cf8824 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -0,0 +1,402 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +/* Ideas to extend this to an icode reader: + + 1. Parse classfile a second time, creating a hashmap `code' that associates method symbols with code. + 2. For every method symbol `meth' in the new scope: + + new = oldclass.info.decl(meth.name).suchThat(old => old.tpe =:= meth.tpe) + + code(new) = code(meth) +*/ +package scala.tools.nsc.symtab.classfile; + +import scala.tools.nsc.util.Position; +import scala.tools.util.AbstractFile; +import scala.tools.util.AbstractFileReader; + +import java.io.IOException; + +abstract class ClassfileParser { + + val global: Global; + import global._; + + import ClassfileConstants._; + import Flags._; + + private var in: AbstractFileReader = _; // the class file + private var clazz: Symbol = _; // the class symbol containing dynamic members + private var staticModule: Symbol = _; // the module symbol containing static members + private var instanceDefs: Scope = _; // the scope of all instance definitions + private var staticDefs: Scope = _; // the scope of all static definitions + private var pool: ConstantPool = _; // the classfile's constant pool + private var isScala: boolean = _; // does class file describe a scala class? + private var hasMeta: boolean = _; // does class file contain jaco meta attribute?s + private var busy: boolean = false; // lock to detect recursive reads + + private object metaParser extends MetaParser { + val global: ClassfileParser.this.global.type = ClassfileParser.this.global + } + + private object unpickler extends UnPickler { + val global: ClassfileParser.this.global.type = ClassfileParser.this.global + } + + def parse(file: AbstractFile, root: Symbol): unit = { + assert(!busy); + busy = true; + this.in = new AbstractFileReader(file); + if (root.isModule) { + this.clazz = root.linkedClass; + this.staticModule = root + } else { + this.clazz = root; + this.staticModule = root.linkedModule + } + this.isScala = false; + this.hasMeta = false; + try { + parseHeader; + this.pool = new ConstantPool; + parseClass() + } catch { + case e: RuntimeException => + if (settings.debug.value) e.printStackTrace(); + throw new IOException("class file '" + in.file + "' is broken") + } + busy = false + } + + private def statics: Symbol = staticModule.moduleClass; + + private def parseHeader: unit = { + val magic = in.nextInt(); + if (magic != JAVA_MAGIC) + throw new IOException("class file '" + in.file + "' " + + "has wrong magic number 0x" + Integer.toHexString(magic) + + ", should be 0x" + Integer.toHexString(JAVA_MAGIC)); + val minorVersion = in.nextChar(); + val majorVersion = in.nextChar(); + if ((majorVersion < JAVA_MAJOR_VERSION) || + ((majorVersion == JAVA_MAJOR_VERSION) && + (minorVersion < JAVA_MINOR_VERSION))) + throw new IOException("class file '" + in.file + "' " + + "has unknown version " + + majorVersion + "." + minorVersion + + ", should be at least " + + JAVA_MAJOR_VERSION + "." + JAVA_MINOR_VERSION); + + } + + class ConstantPool { + private val len = in.nextChar(); + private val starts = new Array[int](len); + private val values = new Array[Object](len); + private val internalized = new Array[Name](len); + { var i = 1; + while (i < starts.length) { + starts(i) = in.bp; + i = i + 1; + in.nextByte() match { + case CONSTANT_UTF8 | CONSTANT_UNICODE => + in.skip(in.nextChar()); + case CONSTANT_CLASS | CONSTANT_STRING => + in.skip(2); + case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT => + in.skip(4); + case CONSTANT_LONG | CONSTANT_DOUBLE => + in.skip(8); + i = i + 1 + case _ => + errorBadTag(in.bp - 1); + } + } + } + + def getName(index: int): Name = { + if (index <= 0 || len <= index) errorBadIndex(index); + var name = values(index).asInstanceOf[Name]; + if (name == null) { + val start = starts(index); + if (in.buf(start) != CONSTANT_UTF8) errorBadTag(start); + name = newTermName(in.buf, start + 3, in.getChar(start + 1)); + values(index) = name; + } + name + } + + def getExternalName(index: int): Name = { + if (index <= 0 || len <= index) errorBadIndex(index); + if (internalized(index) == null) { + internalized(index) = getName(index).replace('/', '.') + } + internalized(index) + } + + def getClassSymbol(index: int): Symbol = { + if (index <= 0 || len <= index) errorBadIndex(index); + var c = values(index).asInstanceOf[Symbol]; + if (c == null) { + val start = starts(index); + if (in.buf(start) != CONSTANT_CLASS) errorBadTag(start); + val name = getExternalName(in.getChar(start + 1)); + c = definitions.getClass(name); + values(index) = c; + } + c + } + + def getType(index: int): Type = + sigToType(getExternalName(index)); + + def getSuperClass(index: int): Symbol = + if (index == 0) definitions.AnyClass else getClassSymbol(index); + + def getConstant(index: int): Constant = { + if (index <= 0 || len <= index) errorBadIndex(index); + var value = values(index); + if (value == null) { + val start = starts(index); + value = in.buf(start) match { + case CONSTANT_STRING => + Constant(getName(in.getChar(start + 1)).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 _ => + errorBadTag(start); + } + values(index) = value; + } + value.asInstanceOf[Constant] + } + + /** Throws an exception signaling a bad constant index. */ + private def errorBadIndex(index: int) = + throw new RuntimeException("bad constant pool index: " + index); + + /** 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); + } + + private def sigToType(name: Name): Type = { + var index = 0; + val end = name.length; + def objToAny(tp: Type): Type = + if (tp.symbol == definitions.ObjectClass) definitions.AnyClass.tpe + else tp; + def paramsigs2types: List[Type] = + if (name(index) == ')') { index = index + 1; List() } + else objToAny(sig2type) :: paramsigs2types; + def sig2type: Type = { + val tag = name(index); index = index + 1; + tag match { + case 'B' => definitions.ByteClass.tpe + case 'C' => definitions.CharClass.tpe + case 'D' => definitions.DoubleClass.tpe + case 'F' => definitions.FloatClass.tpe + case 'I' => definitions.IntClass.tpe + case 'J' => definitions.LongClass.tpe + case 'S' => definitions.ShortClass.tpe + case 'V' => definitions.UnitClass.tpe + case 'Z' => definitions.BooleanClass.tpe + case 'L' => + val start = index; + while (name(index) != ';') { index = index + 1 } + val end = index; + index = index + 1; + definitions.getClass(name.subName(start, end)).tpe + case '[' => + while ('0' <= name(index) && name(index) <= '9') index = index + 1; + appliedType(definitions.ArrayClass.tpe, List(sig2type)) + case '(' => + JavaMethodType(paramsigs2types, sig2type) + } + } + sig2type + } + + def parseClass(): unit = { + val jflags = in.nextChar(); + var sflags = transFlags(jflags); + if ((sflags & DEFERRED) != 0) sflags = sflags & ~DEFERRED | ABSTRACT; + val c = pool.getClassSymbol(in.nextChar()); + if (c != clazz) + throw new IOException("class file '" + in.file + "' contains wrong " + clazz); + val superType = pool.getSuperClass(in.nextChar()).tpe; + val ifaceCount = in.nextChar(); + val parents = (superType :: + (for (val i <- List.range(0, ifaceCount)) + yield pool.getSuperClass(in.nextChar()).tpe)); + instanceDefs = new Scope(); + staticDefs = new Scope(); + val classInfo = ClassInfoType(parents, instanceDefs, clazz); + val staticInfo = ClassInfoType(List(), staticDefs, statics); + + val curbp = in.bp; + skipMembers(); // fields + skipMembers(); // methods + parseAttributes(clazz, classInfo); + if (!isScala) { + clazz.setFlag(sflags); + if (!hasMeta) { + clazz.setInfo(classInfo); + } + statics.setInfo(staticInfo); + staticModule.setInfo(statics.tpe); + staticModule.setFlag(JAVA); + staticModule.moduleClass.setFlag(JAVA); + in.bp = curbp; + val fieldCount = in.nextChar(); + for (val i <- Iterator.range(0, fieldCount)) parseField(); + val methodCount = in.nextChar(); + for (val i <- Iterator.range(0, methodCount)) parseMethod(); + if (instanceDefs.lookup(nme.CONSTRUCTOR) == NoSymbol && (sflags & INTERFACE) == 0) { + //System.out.println("adding constructor to " + clazz);//DEBUG + instanceDefs.enter( + clazz.newConstructor(Position.NOPOS) + .setFlag(clazz.flags & ConstrFlags).setInfo(MethodType(List(), clazz.tpe))); + } + } + } + + def parseField(): unit = { + val jflags = in.nextChar(); + var sflags = transFlags(jflags); + if ((sflags & FINAL) == 0) sflags = sflags | MUTABLE; + if ((sflags & PRIVATE) != 0) { + in.skip(4); skipAttributes(); + } else { + val name = pool.getName(in.nextChar()); + val info = pool.getType(in.nextChar()); + val sym = getOwner(jflags) + .newValue(Position.NOPOS, name).setFlag(sflags).setInfo(info); + parseAttributes(sym, info); + getScope(jflags).enter(sym); + } + } + + def parseMethod(): unit = { + val jflags = in.nextChar(); + var sflags = transFlags(jflags); + if ((sflags & JAVA_ACC_BRIDGE) != 0) sflags = sflags | PRIVATE; + if ((sflags & PRIVATE) != 0) { + in.skip(4); skipAttributes(); + } else { + val name = pool.getName(in.nextChar()); + var info = pool.getType(in.nextChar()); + if (name == nme.CONSTRUCTOR) + info match { + case MethodType(formals, restpe) => + assert(restpe.symbol == definitions.UnitClass); + info = MethodType(formals, clazz.tpe) + } + val sym = getOwner(jflags) + .newMethod(Position.NOPOS, name).setFlag(sflags).setInfo(info); + parseAttributes(sym, info); + getScope(jflags).enter(sym); + } + } + + def parseAttributes(sym: Symbol, symtype: Type): unit = { + def parseAttribute(): unit = { + val attrName = pool.getName(in.nextChar()); + val attrLen = in.nextInt(); + attrName match { + case nme.SyntheticATTR => + sym.setFlag(SYNTHETIC); + in.skip(attrLen) + case nme.BridgeATTR => + sym.setFlag(BRIDGE); + in.skip(attrLen) + case nme.DeprecatedATTR => + sym.setFlag(DEPRECATED); + in.skip(attrLen) + case nme.ConstantValueATTR => + val c = pool.getConstant(in.nextChar()); + val c1 = c convertTo symtype; + sym.setInfo(ConstantType(c1)); + case nme.InnerClassesATTR => + parseInnerClasses() + case nme.ScalaSignatureATTR => + unpickler.unpickle(in.buf, in.bp, clazz, staticModule); + this.isScala = true; + case nme.JacoMetaATTR => + val meta = pool.getName(in.nextChar()).toString().trim(); + metaParser.parse(meta, sym, symtype); + this.hasMeta = true; + case _ => + in.skip(attrLen) + } + } + def parseInnerClasses(): unit = { + for (val i <- Iterator.range(0, in.nextChar())) { + val innerIndex = in.nextChar(); + val outerIndex = in.nextChar(); + val nameIndex = in.nextChar(); + val jflags = in.nextChar(); + if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0 && + (jflags & (JAVA_ACC_PUBLIC | JAVA_ACC_PROTECTED)) != 0 && + pool.getClassSymbol(outerIndex) == sym) { + val innerAlias = getOwner(jflags) + .newAliasType(Position.NOPOS, pool.getName(nameIndex).toTypeName) + .setInfo(pool.getClassSymbol(innerIndex).tpe); + getScope(jflags).enter(innerAlias); + } + } + } + val attrCount = in.nextChar(); + for (val i <- Iterator.range(0, attrCount)) parseAttribute() + } + + def skipAttributes(): unit = { + val attrCount = in.nextChar(); + for (val i <- Iterator.range(0, attrCount)) { + in.skip(2); in.skip(in.nextInt()) + } + } + + def skipMembers(): unit = { + val memberCount = in.nextChar(); + for (val i <- Iterator.range(0, memberCount)) { + in.skip(6); skipAttributes() + } + } + + private def getOwner(flags: int): Symbol = + if ((flags & JAVA_ACC_STATIC) != 0) statics else clazz; + + private def getScope(flags: int): Scope = + if ((flags & JAVA_ACC_STATIC) != 0) staticDefs else instanceDefs; + + private def transFlags(flags: int): long = { + var res = 0l; + if ((flags & JAVA_ACC_PRIVATE) != 0) + res = res | PRIVATE + else if ((flags & JAVA_ACC_PROTECTED) != 0) + res = res | PROTECTED + else if ((flags & JAVA_ACC_PUBLIC) == 0) + res = res | PRIVATE; + if ((flags & JAVA_ACC_ABSTRACT) != 0) + res = res | DEFERRED; + if ((flags & JAVA_ACC_FINAL) != 0) + res = res | FINAL; + if ((flags & JAVA_ACC_INTERFACE) != 0) + res = res | TRAIT | INTERFACE | ABSTRACT; + if ((flags & JAVA_ACC_SYNTHETIC) != 0) + res = res | SYNTHETIC; + if ((flags & JAVA_ACC_STATIC) != 0) + res = res | STATIC; + res | JAVA; + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/MetaParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/MetaParser.scala new file mode 100644 index 0000000000..eca5da7091 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/MetaParser.scala @@ -0,0 +1,153 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab.classfile; + +import java.util.{StringTokenizer, NoSuchElementException} +import util.ListBuffer; +import scala.tools.nsc.util.Position; + +abstract class MetaParser{ + + val global: Global; + import global._; + + private var scanner: StringTokenizer = _; + private var owner: Symbol = _; + private var ownertype: Type = _; + private var token: String = _; + private var locals: Scope = null; + + def parse(meta: String, sym: Symbol, symtype: Type): unit = { + //System.out.println("parse meta for " + sym + ":" + meta + ", locals = " + locals);//DEBUG + this.scanner = new StringTokenizer(meta, "()[], \t<;", true); + this.owner = sym; + this.ownertype = symtype; + nextToken(); + if (token == "class") parseClass() + else if (token == "method") parseMethod() + else if (token == "field") parseField() + else if (token == "constr") parseConstr() + else owner.setInfo(symtype); + } + + protected def nextToken(): unit = + try { + do { token = scanner.nextToken().trim() } while (token.length() == 0) + } catch { + case ex: NoSuchElementException => token = "" + } + + protected def parseType(): Type = { + val str = token; + nextToken(); + val sym = locals.lookup(newTypeName(str)); + if (sym != NoSymbol) sym.tpe + else { + val tp = definitions.getClass(str).tpe; + if (token != "[") tp + else { + val args = new ListBuffer[Type]; + do { + nextToken(); args += parseType(); + } while (token == ","); + nextToken(); + appliedType(tp, args.toList) + } + } + } + + protected def parseTypeParam(): Symbol = { + val vflag = + if (token == "+") { nextToken(); Flags.COVARIANT } + else if (token == "-") { nextToken(); Flags.CONTRAVARIANT } + else 0; + assert(token.startsWith("?")); + val sym = owner.newTypeParameter(Position.NOPOS, newTypeName(token)).setFlag(vflag); + nextToken(); + val lo = if (token == ">") { nextToken(); parseType() } else definitions.AllClass.tpe; + val hi = if (token == "<") { nextToken(); parseType() } else definitions.AnyClass.tpe; + sym.setInfo(TypeBounds(lo, hi)); + locals enter sym; + sym + } + + protected def parseTypeParams(): List[Symbol] = { + nextToken(); + val syms = new ListBuffer[Symbol]; + if (token != "]") { + syms += parseTypeParam(); + while (token == ",") { + nextToken(); syms += parseTypeParam(); + } + } + assert(token == "]"); + syms.toList + } + + protected def parseParams(): List[Type] = { + nextToken(); + val tps = new ListBuffer[Type]; + if (token != ")") { + tps += parseType(); + while (token == ",") { + nextToken(); tps += parseType(); + } + } + assert(token == ")"); + tps.toList + } + + protected def parseClass(): unit = { + locals = new Scope(); + def parse(): Type = { + nextToken(); + if (token == "[") { + PolyType(parseTypeParams(), parse()) + } else if (token == "extends") { + val tps = new ListBuffer[Type]; + do { + nextToken(); tps += parseType() + } while (token == "with"); + ownertype match { + case ClassInfoType(parents, decls, clazz) => + ClassInfoType(tps.toList, decls, clazz) + } + } else ownertype + } + owner.setInfo(parse()); + assert(token == ";") + } + + protected def parseMethod(): unit = { + val globals = locals; + locals = if (locals == null) new Scope() else new Scope(locals); + def parse(): Type = { + nextToken(); + if (token == "[") PolyType(parseTypeParams(), parse()) + else if (token == "(") MethodType(parseParams(), parse()) + else parseType() + } + owner.setInfo(parse()); + locals = globals; + assert(token == ";") + } + + protected def parseField(): unit = { + nextToken(); + owner.setInfo(parseType()); + assert(token == ";") + } + + protected def parseConstr(): unit = { + def parse(): Type = { + nextToken(); + if (token == "(") MethodType(parseParams(), parse()) + else owner.owner.tpe + } + owner.setInfo(parse()); + assert(token == ";") + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala b/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala new file mode 100644 index 0000000000..aed16bf057 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala @@ -0,0 +1,118 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab.classfile; + +/** 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); + System.arraycopy(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.asInstanceOf[byte]; + writeIndex = 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 = { + def writeNatPrefix(x: int): unit = { + val y = x >>> 7; + if (y != 0) writeNatPrefix(y); + writeByte((x & 0x7f) | 0x80); + } + val y = x >>> 7; + if (y != 0) writeNatPrefix(y); + writeByte(x & 0x7f) + } + + /** Write a natural number at `pos' + * If number is more than one byte, shift rest of array to make space. */ + def patchNat(pos: int, x: int): unit = { + def patchNatPrefix(x: int): unit = { + writeByte(0); + System.arraycopy(bytes, pos, bytes, pos+1, writeIndex - (pos+1)); + bytes(pos) = ((x & 0x7f) | 0x80).asInstanceOf[byte]; + val y = x >>> 7; + if (y != 0) patchNatPrefix(y) + } + bytes(pos) = (x & 0x7f).asInstanceOf[byte]; + val y = x >>> 7; + if (y != 0) patchNatPrefix(y); + } + + /** Write a long number in signed big endian format, base 256. */ + def writeLong(x: long): unit = { + val y = x >> 8; + val z = x & 0xff; + if (-y != (z >> 7)) writeLong(y); + writeByte(z.asInstanceOf[int]); + } + + // -- Basic input routines -------------------------------------------- + + /** Read a byte */ + def readByte(): int = { + val x = bytes(readIndex); 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 = { + var b = 0; + var x = 0; + do { + b = readByte(); + x = (x << 7) + (b & 0x7f); + } while ((b & 0x80) != 0); + 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 = i + 1 + } + val leading = 64 - (len << 3); + x << leading >> leading + } + + /** Perform operation `op' until readIndex == end. Concatenate results into a list. */ + def until[T](end: int, op: () => T): List[T] = + if (readIndex == end) List() else op() :: until(end, op); + + /** Create an index */ + def createIndex: Array[int] = { + val index = new Array[int](readNat()); + for (val i <- Iterator.range(0, index.length)) { + index(i) = readIndex; + readByte(); + readIndex = readNat() + readIndex + } + index + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/PickleFormat.scala b/src/compiler/scala/tools/nsc/symtab/classfile/PickleFormat.scala new file mode 100644 index 0000000000..220b4bc6b5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/PickleFormat.scala @@ -0,0 +1,99 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab.classfile; + +object PickleFormat { + +/*************************************************** + * Symbol table attribute format: + * Symtab = nentries_Nat {Entry} + * Entry = 1 TERMNAME len_Nat NameInfo + * | 2 TYPENAME len_Nat NameInfo + * | 3 NONEsym len_Nat + * | 4 TYPEsym len_Nat SymbolInfo + * | 5 ALIASsym len_Nat SymbolInfo + * | 6 CLASSsym len_Nat SymbolInfo [thistype_Ref] + * | 7 MODULEsym len_Nat SymbolInfo + * | 8 VALsym len_Nat SymbolInfo [alias_Ref] + * | 9 EXTref len_Nat name_Ref [owner_Ref] + * | 10 EXTMODCLASSref len_Nat name_Ref [owner_Ref] + * | 11 NOtpe len_Nat + * | 12 NOPREFIXtpe len_Nat + * | 13 THIStpe len_Nat sym_Ref + * | 14 SINGLEtpe len_Nat type_Ref sym_Ref + * | 15 CONSTANTtpe len_Nat type_Ref constant_Ref + * | 16 TYPEREFtpe len_Nat type_Ref sym_Ref {targ_Ref} + * | 17 TYPEBOUNDStpe len_Nat tpe_Ref tpe_Ref + * | 18 REFINEDtpe len_Nat classsym_Ref {tpe_Ref} + * | 19 CLASSINFOtpe len_Nat classsym_Ref {tpe_Ref} + * | 20 METHODtpe len_Nat tpe_Ref {tpe_Ref} + * | 21 POLYTtpe len_Nat tpe_Ref {sym_Ref} + * | 22 IMPLICITMETHODtpe len_Nat tpe_Ref {tpe_Ref} + * | 24 LITERALunit len_Nat + * | 25 LITERALboolean len_Nat value_Long + * | 26 LITERALbyte len_Nat value_Long + * | 27 LITERALshort len_Nat value_Long + * | 28 LITERALchar len_Nat value_Long + * | 29 LITERALint len_Nat value_Long + * | 30 LITERALlong len_Nat value_Long + * | 31 LITERALfloat len_Nat value_Long + * | 32 LITERALdouble len_Nat value_Long + * | 33 LITERALstring len_Nat name_Ref + * | 34 LITERALnull len_Nat + * | 35 LITERALzero len_Nat + * | 36 ATTRIBUTE sym_Ref type_Ref {constant_Ref} <not yet> + * SymbolInfo = name_Ref owner_Ref flags_Nat info_Ref + * NameInfo = <character sequence of length len_Nat in Utf8 format> + * NumInfo = <len_Nat-byte signed number in big endian format> + * Ref = Nat + * + * len is remaining length after `len'. + */ + val MajorVersion = 2; + val MinorVersion = 0; + + final val TERMname = 1; + final val TYPEname = 2; + final val NONEsym = 3; + final val TYPEsym = 4; + final val ALIASsym = 5; + final val CLASSsym = 6; + final val MODULEsym = 7; + final val VALsym = 8; + final val EXTref = 9; + final val EXTMODCLASSref = 10; + final val NOtpe = 11; + final val NOPREFIXtpe = 12; + final val THIStpe = 13; + final val SINGLEtpe = 14; + final val CONSTANTtpe = 15; + final val TYPEREFtpe = 16; + final val TYPEBOUNDStpe = 17; + final val REFINEDtpe = 18; + final val CLASSINFOtpe = 19; + final val METHODtpe = 20; + final val POLYtpe = 21; + final val IMPLICITMETHODtpe = 22; + final val LITERAL = 23; // base line for literals + final val LITERALunit = 24; + final val LITERALboolean = 25; + final val LITERALbyte = 26; + final val LITERALshort = 27; + final val LITERALchar = 28; + final val LITERALint = 29; + final val LITERALlong = 30; + final val LITERALfloat = 31; + final val LITERALdouble = 32; + final val LITERALstring = 33; + final val LITERALnull = 34; + final val LITERALzero = 35; + final val ATTRIBUTE = 40; + + final val firstSymTag = NONEsym; + final val lastSymTag = VALsym; + final val firstTypeTag = NOtpe; + final val lastTypeTag = POLYtpe; +} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala new file mode 100644 index 0000000000..5ecd30fcd1 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -0,0 +1,244 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab.classfile; + +import java.io._; +import java.lang.{Float, Double} +import scala.collection.mutable.HashMap; +import Flags._; +import PickleFormat._; + +/** + * Serialize a top-level module and/or class; + * @see EntryTags.scala for symbol table attribute format. + */ +abstract class Pickler extends SubComponent { + import global._; + + val phaseName = "pickler"; + def newPhase(prev: Phase): StdPhase = new PicklePhase(prev); + + class PicklePhase(prev: Phase) extends StdPhase(prev) { + def apply(unit: CompilationUnit): unit = { + def pickle(tree: Tree): unit = { + + def add(sym: Symbol, pickle: Pickle) = { + if (!sym.isExternal && !currentRun.symData.contains(sym)) { + if (settings.debug.value) log("pickling " + sym); + pickle.putSymbol(sym); + currentRun.symData(sym) = pickle; + } + } + + tree match { + case PackageDef(_, stats) => stats foreach pickle; + case ClassDef(_, _, _, _, _) | ModuleDef(_, _, _) => + val sym = tree.symbol; + val pickle = new Pickle(sym.name.toTermName, sym.owner); + add(sym, pickle); + add(sym.linkedSym, pickle); + pickle.finish + case _ => + } + } + pickle(unit.body); + } + } + + class Pickle(rootName: Name, rootOwner: Symbol) extends PickleBuffer(new Array[byte](4096), -1, 0) { + private var entries = new Array[AnyRef](256); + private var ep = 0; + private val index = new HashMap[AnyRef, int]; + + /** Is root in symbol.owner*? */ + private def isLocal(sym: Symbol): boolean = ( + sym.isRefinementClass || + sym.name.toTermName == rootName && sym.owner == rootOwner || + sym != NoSymbol && isLocal(sym.owner) + ); + + // Phase 1 methods: Populate entries/index ------------------------------------ + + /** Store entry `e' in index at next available position unless it it + * already there. Return true iff entry is new. */ + private def putEntry(entry: AnyRef): boolean = index.get(entry) match { + case Some(_) => false + case None => + if (ep == entries.length) { + val entries1 = new Array[AnyRef](ep * 2); + System.arraycopy(entries, 0, entries1, 0, ep); + entries = entries1; + } + entries(ep) = entry; + index(entry) = ep; + ep = ep + 1; + true + } + + /** Store symbol in index. If symbol is local, also store everything it refers to. */ + def putSymbol(sym: Symbol): unit = if (putEntry(sym)) { + if (isLocal(sym)) { + putEntry(sym.name); + putSymbol(sym.owner); + putType(sym.info); + if (sym.thisSym != sym) + putType(sym.typeOfThis); + putSymbol(sym.alias); + //for (val attr <- sym.attributes) putAttribute(sym, attr); + } else if (sym != NoSymbol) { + putEntry(if (sym.isModuleClass) sym.name.toTermName else sym.name); + if (!sym.owner.isRoot) putSymbol(sym.owner); + } + } + private def putSymbols(syms: List[Symbol]) = syms foreach putSymbol; + + /** Store type and everythig it refers to in index. */ + private def putType(tp: Type): unit = if (putEntry(tp)) { + tp match { + case NoType | NoPrefix => + ; + case ThisType(sym) => + putSymbol(sym) + case SingleType(pre, sym) => + putType(pre); putSymbol(sym) + case ConstantType(value) => + putConstant(value) + case TypeRef(pre, sym, args) => + putType(pre); putSymbol(sym); putTypes(args) + case TypeBounds(lo, hi) => + putType(lo); putType(hi); + case RefinedType(parents, decls) => + putSymbol(tp.symbol); putTypes(parents); putSymbols(decls.toList) + case ClassInfoType(parents, decls, clazz) => + putSymbol(clazz); putTypes(parents); putSymbols(decls.toList) + case MethodType(formals, restpe) => + putType(restpe); putTypes(formals) + case PolyType(tparams, restpe) => + putType(restpe); putSymbols(tparams) + case _ => + throw new FatalError("bad type: " + tp + "(" + tp.getClass() + ")") + } + } + private def putTypes(tps: List[Type]): unit = tps foreach putType; + + private def putConstant(c: Constant) = + if (putEntry(c) && c.tag == StringTag) putEntry(newTermName(c.stringValue)); + +/* + private def putAttribute(attr: AttrInfo): unit = if (putEntry(attr)) { + putType(attr._1); + for (val c <- attr._2) putConstant(c); + } +*/ + // Phase 2 methods: Write all entries to byte array ------------------------------ + + private val buf = new PickleBuffer(new Array[byte](4096), -1, 0); + + /** Write a reference to object, i.e., the object's number in the index. */ + private def writeRef(ref: AnyRef): unit = writeNat(index(ref)); + private def writeRefs(refs: List[AnyRef]): unit = refs foreach writeRef; + + /** Write name, owner, flags, and info of a symbol */ + private def writeSymInfo(sym: Symbol): unit = { + writeRef(sym.name); + writeRef(sym.owner); + writeNat((sym.flags & PickledFlags).asInstanceOf[int]); + writeRef(sym.info) + } + + /** Write a name in Utf8 format. */ + def writeName(name: Name): unit = { + ensureCapacity(name.length * 3); + writeIndex = name.copyUTF8(bytes, writeIndex); + } + + /** Write an entry */ + private def writeEntry(entry: AnyRef): unit = { + def writeBody: int = entry match { + case name: Name => + writeName(name); + if (name.isTermName) TERMname else TYPEname + case NoSymbol => + NONEsym + case sym: Symbol if !isLocal(sym) => + val tag = + if (sym.isModuleClass) { + writeRef(sym.name.toTermName); EXTMODCLASSref + } else { + writeRef(sym.name); EXTref + } + if (!sym.owner.isRoot) writeRef(sym.owner); + tag + case sym: ClassSymbol => + writeSymInfo(sym); + if (sym.thisSym != sym) writeRef(sym.typeOfThis); + CLASSsym + case sym: TypeSymbol => + writeSymInfo(sym); + if (sym.isAbstractType) TYPEsym else ALIASsym + case sym: TermSymbol => + writeSymInfo(sym); + if (sym.alias != NoSymbol) writeRef(sym.alias); + if (sym.isModule) MODULEsym else VALsym + case NoType => + NOtpe + case NoPrefix => + NOPREFIXtpe + case ThisType(sym) => + writeRef(sym); THIStpe + case SingleType(pre, sym) => + writeRef(pre); writeRef(sym); SINGLEtpe + case ConstantType(value) => + writeRef(value); + CONSTANTtpe + case TypeRef(pre, sym, args) => + writeRef(pre); writeRef(sym); writeRefs(args); TYPEREFtpe + case TypeBounds(lo, hi) => + writeRef(lo); writeRef(hi); TYPEBOUNDStpe + case tp @ RefinedType(parents, decls) => + writeRef(tp.symbol); writeRefs(parents); REFINEDtpe + case ClassInfoType(parents, decls, clazz) => + writeRef(clazz); writeRefs(parents); CLASSINFOtpe + case MethodType(formals, restpe) => + writeRef(restpe); writeRefs(formals); + if (entry.isInstanceOf[ImplicitMethodType]) IMPLICITMETHODtpe + else METHODtpe + case PolyType(tparams, restpe) => + writeRef(restpe); writeRefs(tparams); POLYtpe + case c @ Constant(_) => + if (c.tag == BooleanTag) writeLong(if (c.booleanValue) 1 else 0) + else if (ByteTag <= c.tag && c.tag <= LongTag) writeLong(c.longValue) + else if (c.tag == FloatTag) writeLong(Float.floatToIntBits(c.floatValue)) + else if (c.tag == DoubleTag) writeLong(Double.doubleToLongBits(c.doubleValue)); + else if (c.tag == StringTag) writeRef(newTermName(c.stringValue)); + LITERAL + c.tag +/* + case Pair(tp, cs) => + writeRef(tp); + for (val c <- cs) writeRef(cs); + ATTRIBUTE +*/ + case _ => + throw new FatalError("bad entry: " + entry + " " + entry.getClass());//debug + } + val startpos = writeIndex; + writeByte(0); writeByte(0); + patchNat(startpos, writeBody); + patchNat(startpos + 1, writeIndex - (startpos + 2)); + } + + /** Write byte array */ + def finish = { + assert(writeIndex == 0); + writeNat(MajorVersion); + writeNat(MinorVersion); + writeNat(ep); + if (settings.debug.value) log("" + ep + " entries");//debug + for (val i <- Iterator.range(0, ep)) writeEntry(entries(i)) + } + } +} + diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/SymblfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/SymblfileParser.scala new file mode 100644 index 0000000000..cb7e401b1c --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/SymblfileParser.scala @@ -0,0 +1,31 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab.classfile; + +import scala.tools.util.{AbstractFile, AbstractFileReader}; + +import java.io.IOException; + +abstract class SymblfileParser { + + val global: Global; + import global._; + + private var current: AbstractFile = null; // lock to detect recursive reads + + private object unpickler extends UnPickler { + val global: SymblfileParser.this.global.type = SymblfileParser.this.global + } + + def parse(file: AbstractFile, root: Symbol): unit = { + assert(current == null, current); + current = file; + val in = new AbstractFileReader(file); + if (root.isModule) unpickler.unpickle(in.buf, 0, root.linkedClass, root) + else unpickler.unpickle(in.buf, 0, root, root.linkedModule); + current = null + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala new file mode 100644 index 0000000000..70c50e592b --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala @@ -0,0 +1,260 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.symtab.classfile; + +import scala.tools.nsc.util.Position; +import scala.tools.util.UTF8Codec; +import java.lang.{Float, Double}; +import Flags._; +import PickleFormat._; +import collection.mutable.HashMap; + +abstract class UnPickler { + val global: Global; + import global._; + + def unpickle(bytes: Array[byte], offset: int, classRoot: Symbol, moduleRoot: Symbol): unit = try { + new UnPickle(bytes, offset, classRoot, moduleRoot); + } catch { + case ex: Throwable => + ex.printStackTrace();//debug + + throw new RuntimeException("error reading Scala signature of " + classRoot.nameString + ": " + ex.getMessage()); + } + + private class UnPickle(bytes: Array[byte], offset: int, classRoot: Symbol, moduleRoot: Symbol) extends PickleBuffer(bytes, offset, -1) { + if (settings.debug.value) global.log("unpickle " + classRoot + " and " + moduleRoot); + + checkVersion(); + private val index = createIndex; + private val entries = new Array[AnyRef](index.length); + private val symScopes = new HashMap[Symbol, Scope]; + + for (val i <- Iterator.range(0, index.length)) + if (isSymbolEntry(i)) { at(i, readSymbol); () } + + if (settings.debug.value) global.log("unpickled " + classRoot + ":" + classRoot.rawInfo + ", " + moduleRoot + ":" + moduleRoot.rawInfo);//debug + + private def checkVersion() = { + val major = readNat(); + val minor = readNat(); + if (major != MajorVersion || minor > MinorVersion) + throw new TypeError("Scala signature " + classRoot.name + + " has wrong version\n expected: " + + MajorVersion + "." + MinorVersion + + "\n found: " + major + "." + minor) + } + + /** The scope associated with given symbol */ + private def symScope(sym: Symbol) = symScopes.get(sym) match { + case None => val s = new Scope(); symScopes(sym) = s; s + case Some(s) => s + } + + /** Does entry represent an (internal) symbol */ + private def isSymbolEntry(i: int): boolean = { + val tag = bytes(index(i)); + (firstSymTag <= tag && tag <= lastSymTag && + (tag != CLASSsym || !isRefinementSymbolEntry(i))) + } + + /** Does entry represent a refinement symbol? + * pre: Entry is a class symbol + */ + private def isRefinementSymbolEntry(i: int): boolean = { + val savedIndex = readIndex; + readIndex = index(i); + if (readByte() != CLASSsym) assert(false); + readNat(); + val result = readNameRef() == nme.REFINE_CLASS_NAME.toTypeName; + readIndex = savedIndex; + result + } + + /** If entry at `i' is undefined, define it by performing operation `op' with + * readIndex at start of i'th entry. Restore readIndex afterwards. */ + private def at[T <: AnyRef](i: int, op: () => T): T = { + var r = entries(i); + if (r == null) { + val savedIndex = readIndex; + readIndex = index(i); + r = op(); + assert(entries(i) == null, entries(i)); + entries(i) = r; + readIndex = savedIndex; + } + r.asInstanceOf[T] + } + + /** Read a name */ + private def readName(): Name = { + val tag = readByte(); + val len = readNat(); + tag match { + case TERMname => newTermName(bytes, readIndex, len) + case TYPEname => newTypeName(bytes, readIndex, len) + case _ => errorBadSignature("bad name tag: " + tag); + } + } + + /** Read a symbol */ + private def readSymbol(): Symbol = { + val tag = readByte(); + val end = readNat() + readIndex; + var sym: Symbol = NoSymbol; + tag match { + case EXTref | EXTMODCLASSref => + val name = readNameRef(); + val owner = if (readIndex == end) definitions.RootClass else readSymbolRef(); + sym = if (tag == EXTref) owner.info.decl(name) + else if (name.toTermName == nme.ROOT) definitions.RootClass + else owner.info.decl(name).moduleClass; + if (sym == NoSymbol) + errorBadSignature( + "reference " + (if (name.isTypeName) "type " else "value ") + + name.decode + " of " + owner + " refers to nonexisting symbol.") + case NONEsym => + sym = NoSymbol + case _ => + val name = readNameRef(); + val owner = readSymbolRef(); + val flags = readNat(); + val inforef = readNat(); + tag match { + case TYPEsym => + sym = owner.newAbstractType(Position.NOPOS, name); + case ALIASsym => + sym = owner.newAliasType(Position.NOPOS, name); + case CLASSsym => + sym = + if (name == classRoot.name && owner == classRoot.owner) + if ((flags & MODULE) != 0) moduleRoot.moduleClass + else classRoot + else + if ((flags & MODULE) != 0) owner.newModuleClass(Position.NOPOS, name) + else owner.newClass(Position.NOPOS, name); + if (readIndex != end) sym.typeOfThis = new LazyTypeRef(readNat()) + case MODULEsym => + val clazz = at(inforef, readType).symbol; + sym = + if (name == moduleRoot.name && owner == moduleRoot.owner) moduleRoot + else { + assert(clazz.isInstanceOf[ModuleClassSymbol], clazz); + val mclazz = clazz.asInstanceOf[ModuleClassSymbol]; + val m = owner.newModule(Position.NOPOS, name, mclazz); + mclazz.setSourceModule(m); + m + } + case VALsym => + sym = if (name == moduleRoot.name && owner == moduleRoot.owner) moduleRoot.resetFlag(MODULE) + else owner.newValue(Position.NOPOS, name) + case _ => + errorBadSignature("bad symbol tag: " + tag); + } + sym.setFlag(flags); + if (readIndex != end) assert(sym hasFlag (SUPERACCESSOR | PARAMACCESSOR)); + if (sym hasFlag SUPERACCESSOR) assert(readIndex != end); + sym.setInfo( + if (readIndex != end) new LazyTypeRefAndAlias(inforef, readNat()) + else new LazyTypeRef(inforef)); + if (sym.owner.isClass && sym != classRoot && sym != moduleRoot && + !sym.isModuleClass && !sym.isRefinementClass && !sym.isTypeParameter) + symScope(sym.owner) enter sym; + } + sym + } + + /** Read a type */ + private def readType(): Type = { + val tag = readByte(); + val end = readNat() + readIndex; + tag match { + case NOtpe => + NoType + case NOPREFIXtpe => + NoPrefix + case THIStpe => + ThisType(readSymbolRef()) + case SINGLEtpe => + singleType(readTypeRef(), readSymbolRef()) + case CONSTANTtpe => + ConstantType(readConstantRef()) + case TYPEREFtpe => + rawTypeRef(readTypeRef(), readSymbolRef(), until(end, readTypeRef)) + case TYPEBOUNDStpe => + TypeBounds(readTypeRef(), readTypeRef()) + case REFINEDtpe => + val clazz = readSymbolRef(); +/* + val ps = until(end, readTypeRef); + val dcls = symScope(clazz); + new RefinedType(ps, dcls) { override def symbol = clazz } +*/ + new RefinedType(until(end, readTypeRef), symScope(clazz)) { override def symbol = clazz } + case CLASSINFOtpe => + val clazz = readSymbolRef(); + ClassInfoType(until(end, readTypeRef), symScope(clazz), clazz) + case METHODtpe => + val restpe = readTypeRef(); + MethodType(until(end, readTypeRef), restpe) + case IMPLICITMETHODtpe => + val restpe = readTypeRef(); + ImplicitMethodType(until(end, readTypeRef), restpe) + case POLYtpe => + val restpe = readTypeRef(); + PolyType(until(end, readSymbolRef), restpe) + case _ => + errorBadSignature("bad type tag: " + tag); + } + } + + /** Read a constant */ + private def readConstant(): Constant = { + val tag = readByte(); + val len = readNat(); + tag match { + case LITERALunit => Constant(()) + case LITERALboolean => Constant(if (readLong(len) == 0) false else true) + case LITERALbyte => Constant(readLong(len).asInstanceOf[byte]) + case LITERALshort => Constant(readLong(len).asInstanceOf[short]) + case LITERALchar => Constant(readLong(len).asInstanceOf[char]) + case LITERALint => Constant(readLong(len).asInstanceOf[int]) + case LITERALlong => Constant(readLong(len)) + case LITERALfloat => Constant(Float.intBitsToFloat(readLong(len).asInstanceOf[int])) + case LITERALdouble => Constant(Double.longBitsToDouble(readLong(len))) + case LITERALstring => Constant(readNameRef().toString()) + case LITERALnull => Constant(null) + case _ => errorBadSignature("bad constant tag: " + tag) + } + }; + + /** Read a reference to a name, symbol, type or constant */ + private def readNameRef(): Name = at(readNat(), readName); + private def readSymbolRef(): Symbol = at(readNat(), readSymbol); + private def readTypeRef(): Type = at(readNat(), readType); + private def readConstantRef(): Constant = at(readNat(), readConstant); + + private def errorBadSignature(msg: String) = + throw new RuntimeException("malformed Scala signature of " + classRoot.name + " at " + readIndex + "; " + msg); + + private class LazyTypeRef(i: int) extends LazyType { + private val definedAtRun = currentRun; + override def complete(sym: Symbol): unit = { + val tp = at(i, readType); + sym setInfo tp; + if (currentRun != definedAtRun) tp.complete(sym) + } + override def load(sym: Symbol): unit = complete(sym) + } + + private class LazyTypeRefAndAlias(i: int, j: int) extends LazyTypeRef(i) { + override def complete(sym: Symbol): unit = { + super.complete(sym); + sym.asInstanceOf[TermSymbol].setAlias(at(j, readSymbol)); + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala new file mode 100644 index 0000000000..0ddcd4d33d --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -0,0 +1,258 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import symtab._; +import Flags._; +import util.ListBuffer; +import collection.mutable.HashMap; + +abstract class AddInterfaces extends InfoTransform { + import global._; // the global environment + import definitions._; // standard classes and methods + import posAssigner.atPos; // for filling in tree positions + + override def phaseNewFlags: long = lateDEFERRED | lateINTERFACE; + +// Type transformation + + def erasedTypeRef(sym: Symbol): Type; + + private val implClassMap = new HashMap[Symbol, Symbol]; + private val implMethodMap = new HashMap[Symbol, Symbol]; + + override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = { + implClassMap.clear; + implMethodMap.clear; + super.newPhase(prev) + } + + private def needsImplMethod(sym: Symbol): boolean = ( + sym.isMethod && isInterfaceMember(sym) && + (!(sym hasFlag (DEFERRED | SUPERACCESSOR)) || (sym hasFlag lateDEFERRED)) + ); + + private def isInterfaceMember(sym: Symbol): boolean = { + sym.info; // to set lateMETHOD flag if necessary + (sym.isType || + sym.isMethod && !(sym hasFlag (PRIVATE | BRIDGE | LABEL)) && + !sym.isConstructor && !sym.isImplOnly) + } + + def implClass(iface: Symbol): Symbol = implClassMap.get(iface) match { + case Some(c) => c + case None => + atPhase(currentRun.erasurePhase) { + val implName = nme.implClassName(iface.name); + var impl = if (iface.owner.isClass) iface.owner.info.decl(implName) else NoSymbol; + if (impl == NoSymbol) { + impl = iface.cloneSymbolImpl(iface.owner); + impl.name = implName; + if (iface.owner.isClass) iface.owner.info.decls enter impl + } + impl setPos iface.pos; + impl.flags = iface.flags & ~(INTERFACE | lateINTERFACE) | IMPLCLASS; + impl setInfo new LazyImplClassType(iface); + implClassMap(iface) = impl; + if (settings.debug.value) log("generating impl class " + impl + " in " + iface.owner);//debug + impl + } + } + + private class LazyImplClassType(iface: Symbol) extends LazyType { + + def implDecls(implClass: Symbol, ifaceDecls: Scope): Scope = { + val decls = new Scope(); + for (val sym <- ifaceDecls.elements) { + if (isInterfaceMember(sym)) { + if (needsImplMethod(sym)) { + val impl = sym.cloneSymbol(implClass).setInfo(sym.info).resetFlag(lateDEFERRED); + if (!impl.isExternal) implMethodMap(sym) = impl; + decls enter impl; + sym setFlag lateDEFERRED + } + } else { + sym.owner = implClass; + decls enter sym; + } + } + decls + } + + override def complete(sym: Symbol): unit = { + def implType(tp: Type): Type = tp match { + case ClassInfoType(parents, decls, _) => + //ClassInfoType(traitToImplClass(parents) ::: List(iface.tpe), implDecls(sym, decls), sym) + ClassInfoType( + ObjectClass.tpe :: (parents.tail map traitToImplClass) ::: List(iface.tpe), + implDecls(sym, decls), + sym) + case PolyType(tparams, restpe) => + PolyType(tparams, implType(restpe)) + } + sym.setInfo(atPhase(currentRun.erasurePhase)(implType(iface.info))); + } + + override def load(clazz: Symbol): unit = complete(clazz) + } + + private def traitToImplClass(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) if (sym.needsImplClass) => + typeRef(pre, implClass(sym), args) + case _ => + tp + } + + def transformTraitInfo(tp: Type): Type = tp match { + case ClassInfoType(parents, decls, clazz) => + if (clazz.needsImplClass) { + clazz setFlag lateINTERFACE; + implClass(clazz) // generate an impl class + } + val parents1 = + if (parents.isEmpty) List() + else { + assert(!parents.head.symbol.isTrait || clazz == RepeatedParamClass, clazz); + if (clazz hasFlag INTERFACE) erasedTypeRef(ObjectClass) :: parents.tail + else if (clazz.isImplClass || clazz == ArrayClass) parents + else parents map traitToImplClass + } + val decls1 = if (clazz hasFlag INTERFACE) new Scope(decls.toList filter isInterfaceMember) + else decls; + if ((parents1 eq parents) && (decls1 eq decls)) tp + else ClassInfoType(parents1, decls1, clazz) + case _ => + tp + } + +// Tree transformation -------------------------------------------------------------- + + private class ChangeOwnerAndReturnTraverser(oldowner: Symbol, newowner: Symbol) + extends ChangeOwnerTraverser(oldowner, newowner) { + override def traverse(tree: Tree): unit = { + tree match { + case Return(expr) => + if (tree.symbol == oldowner) tree.symbol = newowner; + case _ => + } + super.traverse(tree) + } + } + + private def ifaceMemberDef(tree: Tree): Tree = + if (!tree.isDef || !isInterfaceMember(tree.symbol)) EmptyTree + else if (needsImplMethod(tree.symbol)) DefDef(tree.symbol, vparamss => EmptyTree) + else tree; + + private def ifaceTemplate(templ: Template): Template = + copy.Template(templ, templ.parents, templ.body map ifaceMemberDef); + + private def implMethodDef(tree: Tree, ifaceMethod: Symbol): Tree = + implMethodMap.get(ifaceMethod) match { + case Some(implMethod) => + tree.symbol = implMethod; + new ChangeOwnerAndReturnTraverser(ifaceMethod, implMethod)(tree) + case None => + throw new Error("implMethod missing for " + ifaceMethod) + } + + private def implMemberDef(tree: Tree): Tree = + if (!tree.isDef || !isInterfaceMember(tree.symbol)) tree + else if (needsImplMethod(tree.symbol)) implMethodDef(tree, tree.symbol) + else EmptyTree; + + private def implTemplate(clazz: Symbol, templ: Template): Template = atPos(templ.pos){ + val templ1 = Template(templ.parents, templ.body map implMemberDef) + .setPos(templ.pos) + .setSymbol(clazz.newLocalDummy(templ.pos)); + new ChangeOwnerTraverser(templ.symbol.owner, clazz)( + new ChangeOwnerTraverser(templ.symbol, templ1.symbol)(templ1)) + } + + def implClassDefs(trees: List[Tree]): List[Tree] = { + val buf = new ListBuffer[Tree]; + for (val tree <- trees) + tree match { + case ClassDef(_, _, _, _, impl) => + if (tree.symbol.needsImplClass) + buf += { + val clazz = implClass(tree.symbol).initialize; + ClassDef(clazz, implTemplate(clazz, impl)) + } + case _ => + } + buf.toList + } + + protected val traitTransformer = new Transformer { + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = + (super.transformStats(stats, exprOwner) ::: + super.transformStats(implClassDefs(stats), exprOwner)); + override def transform(tree: Tree): Tree = { + val tree1 = tree match { + case ClassDef(mods, name, tparams, tpt, impl) => + if (tree.symbol.needsImplClass) { + implClass(tree.symbol).initialize; // to force lateDEFERRED flags + copy.ClassDef(tree, mods | INTERFACE, name, tparams, tpt, ifaceTemplate(impl)) + } + else tree + case Template(parents, body) => + val parents1 = tree.symbol.owner.info.parents map (t => TypeTree(t) setPos tree.pos); + copy.Template(tree, parents1, body) + case This(_) => + if (tree.symbol.needsImplClass) { + val impl = implClass(tree.symbol); + var owner = currentOwner; + while (owner != tree.symbol && owner != impl) owner = owner.owner; + if (owner == impl) This(impl) setPos tree.pos + else tree + } else tree + case Super(qual, mix) => + val mix1 = + if (mix == nme.EMPTY.toTypeName) mix + else { + val ps = atPhase(currentRun.erasurePhase) { + tree.symbol.info.parents dropWhile (p => p.symbol.name != mix) + } + assert(!ps.isEmpty, tree); + if (ps.head.symbol.needsImplClass) implClass(ps.head.symbol).name + else mix + } + if (tree.symbol.needsImplClass) Super(implClass(tree.symbol), mix1) setPos tree.pos + else copy.Super(tree, qual, mix1) + case _ => + tree + } + super.transform(tree1) + } + } +} +/* + val ensureNoEscapes = new TypeTraverser { + def ensureNoEscape(sym: Symbol): unit = { + if (sym.hasFlag(PRIVATE)) { + var o = currentOwner; + while (o != NoSymbol && o != sym.owner && !o.isLocal && !o.hasFlag(PRIVATE)) + o = o.owner; + if (o == sym.owner) sym.makeNotPrivate(base); + } + } + def traverse(t: Type): TypeTraverser = { + t match { + case TypeRef(qual, sym, args) => + ensureNoEscape(sym); + mapOver(t); + case ClassInfoType(parents, decls, clazz) => + parents foreach { p => traverse; () } + traverse(t.typeOfThis); + case _ => + mapOver(t) + } + this + } + } + +*/ diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala new file mode 100644 index 0000000000..ea31249d30 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -0,0 +1,163 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import symtab._; +import Flags._; +import util.{ListBuffer, TreeSet} + +abstract class Constructors extends Transform { + import global._; + import definitions._; + import posAssigner.atPos; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "constructors"; + + protected def newTransformer(unit: CompilationUnit): Transformer = new ConstructorTransformer; + + class ConstructorTransformer extends Transformer { + + def transformClassTemplate(impl: Template): Template = { + val clazz = impl.symbol.owner; + val stats = impl.body; + val localTyper = typer.atOwner(impl, clazz); + var constr: DefDef = null; + var constrParams: List[Symbol] = null; + var constrBody: Block = null; + // decompose primary constructor into the three entities above. + for (val stat <- stats) { + stat match { + case ddef @ DefDef(_, _, _, List(vparams), _, rhs @ Block(_, Literal(_))) => + if (ddef.symbol.isPrimaryConstructor) { + constr = ddef; + constrParams = vparams map (.symbol); + constrBody = rhs + } + case _ => + } + } + + val paramAccessors = clazz.constrParamAccessors; + def parameter(acc: Symbol) = { + val accname = nme.getterName(acc.originalName); + val ps = constrParams.filter { param => accname == param.name } + if (ps.isEmpty) assert(false, "" + accname + " not in " + constrParams); + ps.head + } + + var thisRefSeen: boolean = false; + + val intoConstructorTransformer = new Transformer { + override def transform(tree: Tree): Tree = tree match { + case Apply(Select(This(_), _), List()) + if ((tree.symbol hasFlag PARAMACCESSOR) && tree.symbol.owner == clazz) => + gen.Ident(parameter(tree.symbol.accessed)) setPos tree.pos; + case Select(This(_), _) + if ((tree.symbol hasFlag PARAMACCESSOR) && tree.symbol.owner == clazz) => + gen.Ident(parameter(tree.symbol)) setPos tree.pos; + case This(_) => + thisRefSeen = true; + super.transform(tree) + case Super(_, _) => + thisRefSeen = true; + super.transform(tree) + case _ => + super.transform(tree) + } + } + + def intoConstructor(oldowner: Symbol, tree: Tree) = + intoConstructorTransformer.transform( + new ChangeOwnerTraverser(oldowner, constr.symbol)(tree)); + + def canBeMoved(tree: Tree) = tree match { + case ValDef(_, _, _, _) => !thisRefSeen + case _ => false + } + + def mkAssign(to: Symbol, from: Tree): Tree = + atPos(to.pos) { + localTyper.typed { + Assign(Select(This(clazz), to), from) + } + } + + val defBuf = new ListBuffer[Tree]; + val constrStatBuf = new ListBuffer[Tree]; + val constrPrefixBuf = new ListBuffer[Tree]; + constrBody.stats foreach (constrStatBuf +=); + + for (val stat <- stats) stat match { + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + stat.symbol.tpe match { + case MethodType(List(), tp @ ConstantType(c)) => + defBuf += copy.DefDef( + stat, mods, name, tparams, vparamss, tpt, + Literal(c) setPos rhs.pos setType tp) + case _ => + if (!stat.symbol.isPrimaryConstructor) defBuf += stat + } + case ValDef(mods, name, tpt, rhs) => + if (stat.symbol.tpe.isInstanceOf[ConstantType]) + assert(stat.symbol.getter(stat.symbol.owner) != NoSymbol, stat) + else { + if (rhs != EmptyTree) { + val rhs1 = intoConstructor(stat.symbol, rhs); + (if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign( + stat.symbol, rhs1) + } + defBuf += copy.ValDef(stat, mods, name, tpt, EmptyTree) + } + case ClassDef(_, _, _, _, _) => + defBuf += (new ConstructorTransformer).transform(stat) + case _ => + constrStatBuf += intoConstructor(impl.symbol, stat) + } + + val accessed = new TreeSet[Symbol]((x, y) => x isLess y); + + def isAccessed(sym: Symbol) = ( + sym.owner != clazz || + !(sym hasFlag PARAMACCESSOR) || + !(sym hasFlag LOCAL) || + (accessed contains sym) + ); + + val accessTraverser = new Traverser { + override def traverse(tree: Tree) = { + tree match { + case Select(_, _) => + if (!isAccessed(tree.symbol)) accessed addEntry tree.symbol; + case _ => + } + super.traverse(tree) + } + } + + for (val stat <- defBuf.elements) accessTraverser.traverse(stat); + + val paramInits = for (val acc <- paramAccessors; isAccessed(acc)) + yield mkAssign(acc, Ident(parameter(acc))); + + defBuf += copy.DefDef( + constr, constr.mods, constr.name, constr.tparams, constr.vparamss, constr.tpt, + copy.Block( + constrBody, + paramInits ::: constrPrefixBuf.toList ::: constrStatBuf.toList, + constrBody.expr)); + + copy.Template(impl, impl.parents, defBuf.toList filter (stat => isAccessed(stat.symbol))) + } + + override def transform(tree: Tree): Tree = tree match { + case ClassDef(mods, name, tparams, tpt, impl) if !tree.symbol.hasFlag(INTERFACE) => + copy.ClassDef(tree, mods, name, tparams, tpt, transformClassTemplate(impl)) + case _ => + super.transform(tree) + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala new file mode 100644 index 0000000000..c726d2255d --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -0,0 +1,569 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import collection.mutable.HashMap; +import symtab._; +import Flags._; +import scala.tools.nsc.util.Position; +import util.ListBuffer; + +abstract class Erasure extends AddInterfaces with typechecker.Analyzer { + import global._; // the global environment + import definitions._; // standard classes and methods + import typer.{typed}; // methods to type trees + import posAssigner.atPos; // for filling in tree positions + + val phaseName: String = "erasure"; + def newTransformer(unit: CompilationUnit): Transformer = new ErasureTransformer(unit); + +// -------- erasure on types -------------------------------------------------------- + + /** The erasure |T| of a type T. This is: + * - For a constant type, itself. + * - For a type-bounds structure, the erasure of its upper bound. + * - For every other singleton type, the erasure of its supertype. + * - For a typeref scala.Array[T] where T is an abstract type, scala.runtime.BoxedArray. + * - For a typeref scala.Array[T] where T is not an abstract type, scala.Array[|T|]. + * - For a typeref scala.Any or scala.AnyVal, java.lang.Object. + * - For a typeref scala.Unit, scala.runtime.BoxedUnit. + * - For a typeref P.C[Ts] where C refers to a class, |P|.C. + * - For a typeref P.C[Ts] where C refers to an alias type, the erasure of C's alias. + * - For a typeref P.C[Ts] where C refers to an abstract type, the erasure of C's upper bound. + * - For a non-empty type intersection (possibly with refinement), the erasure of its first parent. + * - For an empty type intersection, java.lang.Object + * - For a method type (Fs)scala.Unit, (|Fs|)scala#Unit. + * - For any other method type (Fs)Y, (|Fs|)|T|. + * - For a polymorphic type, the erasure of its result type + * - 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 all other types, the type itself (with any sub-components erased) + */ + private val erasure = new TypeMap { + def apply(tp: Type): Type = tp match { + case ConstantType(_) => + tp + case st: SubType => + apply(st.supertype) + case TypeRef(pre, sym, args) => + if (sym == ArrayClass) + args.head match { + case TypeRef(_, tvar, _) if (tvar.isAbstractType) => erasedTypeRef(BoxedArrayClass) + case _ => typeRef(apply(pre), sym, args map this) + } + else if (sym == AnyClass || sym == AnyValClass) erasedTypeRef(ObjectClass) + else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass) + else if (sym.isClass) typeRef(apply(pre), sym, List()) + else apply(sym.info) + case PolyType(tparams, restpe) => + apply(restpe) + case MethodType(formals, restpe) => + MethodType( + formals map apply, + if (restpe.symbol == UnitClass) erasedTypeRef(UnitClass) else apply(restpe)); + case RefinedType(parents, decls) => + if (parents.isEmpty) erasedTypeRef(ObjectClass) + else apply(parents.head) + case ClassInfoType(parents, decls, clazz) => + ClassInfoType( + if ((clazz == ObjectClass) || (isValueClass(clazz))) List() + else if (clazz == ArrayClass) List(erasedTypeRef(ObjectClass)) + else removeDoubleObject(parents map this), + decls, clazz) + case _ => + mapOver(tp) + } + } + + /** Type reference after erasure */ + def erasedTypeRef(sym: Symbol): Type = typeRef(erasure(sym.owner.tpe), sym, List()); + + /** Remove duplicate references to class Object in a list of parent classes + * todo: needed? + */ + private def removeDoubleObject(tps: List[Type]): List[Type] = tps match { + case List() => List() + case tp :: tps1 => + if (tp.symbol == ObjectClass) tp :: tps1.filter(.symbol.!=(ObjectClass)) + else tp :: removeDoubleObject(tps1) + } + + /** The symbol's erased info. This is the type's erasure, except for the following symbols + * - For $asInstanceOf : [T]T + * - For $isInstanceOf : [T]scala#Boolean + * - For class Array : [T]C where C is the erased classinfo of the Array class + * - For Array[T].<init> : {scala#Int)Array[T] + * - For a type parameter : A type bounds type consisting of the erasures of its bounds. + */ + def transformInfo(sym: Symbol, tp: Type): Type = + if (sym == Object_asInstanceOf) + sym.info + else if (sym == Object_isInstanceOf || sym == ArrayClass) + PolyType(sym.info.typeParams, erasure(sym.info.resultType)) + else if (sym.isAbstractType) + TypeBounds(WildcardType, WildcardType) + else if (sym.isTerm && sym.owner == ArrayClass) { + if (sym.isClassConstructor) + tp match { + case MethodType(formals, TypeRef(pre, sym, args)) => + MethodType(formals map erasure, typeRef(erasure(pre), sym, args)) + } + else if (sym.name == nme.apply) + tp + else if (sym.name == nme.update) + tp match { + case MethodType(List(index, tvar), restpe) => + MethodType(List(erasure(index), tvar), erasedTypeRef(UnitClass)) + } + else erasure(tp) + } else + transformTraitInfo(erasure(tp)); + +// -------- boxing/unboxing -------------------------------------------------------- + + override def newTyper(context: Context) = new Eraser(context); + + /** The modifier typer which retypes with erased types. */ + class Eraser(context: Context) extends Typer(context) { + + private def evalOnce(expr: Tree, within: (() => Tree) => Tree): Tree = + if (treeInfo.isPureExpr(expr)) { + within(() => expr); + } else { + val temp = context.owner.newValue(expr.pos, context.unit.fresh.newName()) + .setFlag(SYNTHETIC).setInfo(expr.tpe); + Block(List(ValDef(temp, expr)), within(() => Ident(temp) setType expr.tpe)) + } + + /** Box `tree' of unboxed type */ + private def box(tree: Tree): Tree = + typed { + atPos(tree.pos) { + val sym = tree.tpe.symbol; + if (sym == UnitClass) { + if (treeInfo.isPureExpr(tree)) gen.mkRef(BoxedUnit_UNIT) + else Block(List(tree), gen.mkRef(BoxedUnit_UNIT)) + } else if (sym == ArrayClass) { + val elemClass = tree.tpe.typeArgs.head.symbol; + val boxedClass = if (isValueClass(elemClass)) boxedArrayClass(elemClass) + else BoxedObjectArrayClass; + Apply(Select(New(TypeTree(boxedClass.tpe)), nme.CONSTRUCTOR), List(tree)) + } else { + val boxedModule = boxedClass(tree.tpe.symbol).linkedModule; + Apply(Select(gen.mkRef(boxedModule), nme.box), List(tree)) + } + } + } + + /** The method-name xxxValue, where Xxx is a numeric value class name */ + def unboxOp(tp: Type) = { + val clazzName = tp.symbol.name.toString(); + (String.valueOf((clazzName.charAt(0) + ('a' - 'A')).asInstanceOf[char]) + + clazzName.substring(1) + "Value") + } + + /** Unbox `tree' of boxed type to expected type `pt' */ + private def unbox(tree: Tree, pt: Type): Tree = + typed { + atPos(tree.pos) { + if (pt.symbol == UnitClass) { + if (treeInfo.isPureExpr(tree)) Literal(()) + else Block(List(tree), Literal(())) + } else if (pt.symbol == BooleanClass) { + val tree1 = adaptToType(tree, boxedClass(BooleanClass).tpe); + Apply(Select(tree1, "booleanValue"), List()) + } else if (pt.symbol == ArrayClass) { + val tree1 = adaptToType(tree, BoxedArrayClass.tpe); + val elemClass = pt.typeArgs.head.symbol; + val elemTag = + if (isValueClass(elemClass)) + Apply( + Select(gen.mkRef(ScalaRunTimeModule), newTermName(elemClass.name.toString() + "Tag")), + List()) + else Literal(signature(pt.typeArgs.head)); + Apply(Select(tree1, "unbox"), List(elemTag)) + } else { + assert(isNumericValueClass(pt.symbol)); + val tree1 = adaptToType(tree, BoxedNumberClass.tpe); + Apply(Select(tree1, unboxOp(pt)), List()) + } + } + } + + private def cast(tree: Tree, pt: Type): Tree = + if (pt.symbol == ArrayClass && tree.tpe.symbol == ObjectClass) + typed { + atPos(tree.pos) { + evalOnce(tree, x => + gen.cast( + If( + Apply( + TypeApply( + Select(x(), Object_isInstanceOf), + List(TypeTree(BoxedArrayClass.tpe))), + List()), + unbox(x(), pt), + x()), + pt)) + } + } + else gen.cast(tree, pt); + + /** Is symbol a member of unboxed arrays (which will be expanded directly later)? */ + private def isUnboxedArrayMember(sym: Symbol) = ( + sym.name == nme.apply || sym.name == nme.length || sym.name == nme.update || + sym.owner == ObjectClass + ); + + /** Is symbol a member of a boxed value class (which will not be expanded later)? */ + def isBoxedValueMember(sym: Symbol) = + (sym.name == nme.equals_ || sym.name == nme.hashCode_ || sym.name == nme.toString_ || + (sym.name == nme.EQ || sym.name == nme.NE) && sym.info.paramTypes.head.symbol == ObjectClass || + sym == Object_isInstanceOf || sym == Object_asInstanceOf); + + /** Adapt `tree' to expected type `pt' */ + private def adaptToType(tree: Tree, pt: Type): Tree = { + if (settings.debug.value && pt != WildcardType) log("adapting " + tree + ":" + tree.tpe + " to " + pt);//debug + if (tree.tpe <:< pt) + tree + else if (isUnboxedClass(tree.tpe.symbol) && !isUnboxedClass(pt.symbol)) + adaptToType(box(tree), pt) + else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.paramTypes.isEmpty) { + assert(tree.symbol.isStable); + adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) + } else if (pt <:< tree.tpe) + cast(tree, pt) + else if (isUnboxedClass(pt.symbol) && !isUnboxedClass(tree.tpe.symbol)) + adaptToType(unbox(tree, pt), pt) + else + cast(tree, pt) + } + + /** Replace member references as follows: + * - `x == y' for `==' in class Any becomes `x equals y' with `equals' in class Object + * - `x != y' for `!=' in class Any becomes `!(x equals y)' with `equals' in class Object + * - `new BoxedArray.<init>(len)' becomes `new BoxedAnyArray.<init>(len): BoxedArray' + * (the widening typing is necessary so that subsequent member symbols stay the same) + * - `x.asInstanceOf[T]' and `x.asInstanceOf$erased[T]' become `x.$asInstanceOf[T]' + * - `x.isInstanceOf[T]' and `x.isInstanceOf$erased[T]' become `x.$isInstanceOf[T]' + * - `x.m' where `m' is some other member of Any becomes `x.m' where m is a member of class Object + * - `x.m' where `x' has unboxed value type `T' and `m' is not a directly + * translated member of `T' becomes T.box(x).m + * - `x.m' where `x' has type `Array[T]' and `m' is not a directly + * translated member of `Array' becomes new BoxedTArray.<init>(x).m + * - `x.m' where `x' is a reference type and `m' is a directly translated member of value type + * T becomes x.TValue().m + * - All forms of `x.m' where `x' is a boxed type and `m' is a member of an unboxed class + * become `x.m' where `m' is the corresponding member of the boxed class. + */ + private def adaptMember(tree: Tree): Tree = { + tree match { + case Apply(Select(New(tpt), name), args) if (tpt.tpe.symbol == BoxedArrayClass) => + assert(name == nme.CONSTRUCTOR); + atPos(tree.pos) { + Typed(Apply(Select(New(TypeTree(BoxedAnyArrayClass.tpe)), name), args), tpt) + } + case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) + if ((tree.symbol == Any_asInstanceOf || tree.symbol == Any_asInstanceOfErased)) => + val qual1 = typedQualifier(qual); + val targClass = targ.tpe.symbol; + val qualClass = qual1.tpe.symbol; + if (isNumericValueClass(qualClass) && isNumericValueClass(targClass)) + // convert numeric type casts + atPos(tree.pos)(Apply(Select(qual1, "to" + targClass.name), List())) + else if (isValueClass(targClass) || + (targClass == ArrayClass && (qualClass isSubClass BoxedArrayClass))) + unbox(qual1, targ.tpe) + else if (targClass == ArrayClass && qualClass == ObjectClass) + cast(qual1, targ.tpe) + else + tree + case Select(qual, name) if (name != nme.CONSTRUCTOR) => + if (tree.symbol == Any_asInstanceOf || tree.symbol == Any_asInstanceOfErased) + adaptMember(atPos(tree.pos)(Select(qual, Object_asInstanceOf))) + else if (tree.symbol == Any_isInstanceOf || tree.symbol == Any_isInstanceOfErased) + adaptMember(atPos(tree.pos)(Select(qual, Object_isInstanceOf))) + else if (tree.symbol != NoSymbol && tree.symbol.owner == AnyClass) + adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name)))) + else { + var qual1 = typedQualifier(qual); + if ((isValueClass(qual1.tpe.symbol) && isBoxedValueMember(tree.symbol)) || + (qual1.tpe.symbol == ArrayClass && !isUnboxedArrayMember(tree.symbol))) { + qual1 = box(qual1); + } else if (!isValueClass(qual1.tpe.symbol) && + tree.symbol != NoSymbol && + isValueClass(tree.symbol.owner) && + !isBoxedValueMember(tree.symbol)) { + qual1 = unbox(qual1, tree.symbol.owner.tpe) + } + if (tree.symbol != NoSymbol) + if (isUnboxedClass(tree.symbol.owner) && !isUnboxedClass(qual1.tpe.symbol)) + tree.symbol = NoSymbol + else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.paramTypes.isEmpty) { + assert(qual1.symbol.isStable); + qual1 = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType; + } else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.symbol isSubClass tree.symbol.owner))) + qual1 = cast(qual1, tree.symbol.owner.tpe); + copy.Select(tree, qual1, name) + } + case _ => + tree + } + } + + /** A replacement for the standard typer's `adapt' method */ + override protected def adapt(tree: Tree, mode: int, pt: Type): Tree = adaptToType(tree, pt); + + /** A replacement for the standard typer's `typed1' method */ + override protected def typed1(tree: Tree, mode: int, pt: Type): Tree = { + val tree1 = super.typed1(adaptMember(tree), mode, pt); + def adaptCase(cdef: CaseDef): CaseDef = { + val body1 = adaptToType(cdef.body, tree1.tpe); + copy.CaseDef(cdef, cdef.pat, cdef.guard, body1) setType body1.tpe + } + def adaptBranch(branch: Tree): Tree = + if (branch == EmptyTree) branch else adaptToType(branch, tree1.tpe); + tree1 match { + case If(cond, thenp, elsep) => + copy.If(tree1, cond, adaptBranch(thenp), adaptBranch(elsep)) + case Match(selector, cases) => + copy.Match(tree1, selector, cases map adaptCase) + case Try(block, catches, finalizer) => + copy.Try(tree1, adaptBranch(block), catches map adaptCase, finalizer) + case _ => + tree1 + } + } + } + + /** The erasure transformer */ + class ErasureTransformer(unit: CompilationUnit) extends Transformer { + + /** Emit an error if there is a double definition. This can happen in the following + * circumstances: + * - A template defines two members with the same name and erased type. + * - A template defines and inherits two members `m' with different types, + * but their erased types are the same. + * - A template inherits two members `m' with different types, + * but their erased types are the same. + */ + private def checkNoDoubleDefs(root: Symbol): unit = { + def doubleDefError(sym1: Symbol, sym2: Symbol) = { + val tpe1 = atPhase(currentRun.refchecksPhase.next)(root.thisType.memberType(sym1)); + val tpe2 = atPhase(currentRun.refchecksPhase.next)(root.thisType.memberType(sym2)); + unit.error( + if (sym1.owner == root) sym1.pos else root.pos, + (if (sym1.owner == sym2.owner) "double definition:\n" + else if (sym1.owner == root) "name clash between defined and inherited member:\n" + else "name clash between inherited members:\n") + + sym1 + ":" + tpe1 + + (if (sym1.owner == root) "" else sym1.locationString) + " and\n" + + sym2 + ":" + tpe2 + + (if (sym2.owner == root) " at line " + Position.line(unit.source, sym2.pos) else sym2.locationString) + + "\nhave same type" + + (if (tpe1 =:= tpe2) "" else " after erasure: " + atPhase(phase.next)(sym1.tpe))) + } + + val decls = root.info.decls; + var e = decls.elems; + while (e != null) { + if (e.sym.isTerm && !e.sym.isConstructor) { + var e1 = decls.lookupNextEntry(e); + while (e1 != null) { + if (atPhase(phase.next)(e1.sym.info =:= e.sym.info)) doubleDefError(e.sym, e1.sym); + e1 = decls.lookupNextEntry(e1) + } + } + e = e.next + } + + val opc = new overridingPairs.Cursor(root) { + override def exclude(sym: Symbol): boolean = + !sym.isTerm || (sym hasFlag (PRIVATE | BRIDGE)) || super.exclude(sym); + override def matches(sym1: Symbol, sym2: Symbol): boolean = + atPhase(phase.next)(sym1.tpe =:= sym2.tpe) + } + while (opc.hasNext) { + if (!atPhase(currentRun.refchecksPhase.next)( + root.thisType.memberType(opc.overriding) matches + root.thisType.memberType(opc.overridden))) { + if (settings.debug.value) log("" + opc.overriding.locationString + " " + opc.overriding.infosString + opc.overridden.locationString + " " + opc.overridden.infosString); + doubleDefError(opc.overriding, opc.overridden) + } + opc.next + } + } + +/* + for (val bc <- root.info.baseClasses.tail; val other <- bc.info.decls.toList) { + if (other.isTerm && !other.isConstructor && !(other hasFlag (PRIVATE | BRIDGE))) { + for (val member <- root.info.nonPrivateMember(other.name).alternatives) { + if (member != other && + !(member hasFlag BRIDGE) && + atPhase(phase.next)(member.tpe =:= other.tpe) && + !atPhase(refchecksPhase.next)( + root.thisType.memberType(member) matches root.thisType.memberType(other))) { + if (settings.debug.value) log("" + member.locationString + " " + member.infosString + other.locationString + " " + other.infosString); + doubleDefError(member, other) + } + } + } + } +*/ + + /** Add bridge definitions to a template. This means: + * If there is a concrete member `m' which overrides a member in a base class of the template, + * and the erased types of the two members differ, + * and the two members are not inherited or defined by some parent class of the template, + * then a bridge from the overridden member `m1' to the member `m0' is added. + * The bridge has the erased type of `m1' and forwards to `m0'. + * No bridge is added if there is already a bridge to `m0' with the erased type of `m1' + * in the template. + */ + private def bridgeDefs(owner: Symbol): List[Tree] = { + //System.out.println("computing bridges for " + owner);//DEBUG + val site = owner.thisType; + val bridgesScope = new Scope(); + val bridgeTarget = new HashMap[Symbol, Symbol]; + var bridges: List[Tree] = List(); + val opc = atPhase(phase.prev) { // to avoid DEFERRED flags for interfaces + new overridingPairs.Cursor(owner) { + override def parents: List[Type] = List(owner.info.parents.head); + override def exclude(sym: Symbol): boolean = + !sym.isMethod || (sym hasFlag (PRIVATE | BRIDGE)) || super.exclude(sym); + } + } + while (opc.hasNext) { + val member = opc.overriding; + val other = opc.overridden; + //System.out.println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString);//DEBUG + if (!atPhase(phase.prev)(member hasFlag DEFERRED)) { + val otpe = erasure(other.tpe); + val bridgeNeeded = atPhase(phase.next) ( + !(other.tpe =:= member.tpe) && + { var e = bridgesScope.lookupEntry(member.name); + while (e != null && !((e.sym.tpe =:= otpe) && (bridgeTarget(e.sym) == member))) + e = bridgesScope.lookupNextEntry(e); + e == null + } + ); + if (bridgeNeeded) { + val bridge = other.cloneSymbolImpl(owner) + .setPos(owner.pos) + .setFlag(member.flags | BRIDGE) + .resetFlag(ACCESSOR | DEFERRED | lateDEFERRED) + .setInfo(otpe); + bridgeTarget(bridge) = member; + owner.info.decls.enter(bridge); + bridgesScope enter bridge; + bridges = + atPhase(phase.next) { + atPos(bridge.pos) { + val bridgeDef = + DefDef(bridge, vparamss => + member.tpe match { + case MethodType(List(), ConstantType(c)) => Literal(c) + case _ => + (((Select(This(owner), member): Tree) /: vparamss) + ((fun, vparams) => Apply(fun, vparams map Ident))) + }); + if (settings.debug.value) + log("generating bridge from " + other + "(" + Flags.flagsToString(bridge.flags) + ")" + ":" + otpe + other.locationString + " to " + member + ":" + erasure(member.tpe) + member.locationString + " =\n " + bridgeDef); + bridgeDef + } + } :: bridges; + } + } + opc.next + } + bridges + } +/* + for (val bc <- site.baseClasses.tail; val other <- bc.info.decls.toList) { + if (other.isMethod && !other.isConstructor) { + for (val member <- site.nonPrivateMember(other.name).alternatives) { + if (member != other && + !(member hasFlag DEFERRED) && + (site.memberType(member) matches site.memberType(other)) && + !(site.parents exists (p => + (p.symbol isSubClass member.owner) && (p.symbol isSubClass other.owner)))) { +... + } + } +*/ + + def addBridges(stats: List[Tree], base: Symbol): List[Tree] = + if (base.isTrait) stats + else { + val bridges = bridgeDefs(base); + if (bridges.isEmpty) stats else stats ::: bridges + } + + /** Transform tree at phase `erasure' before retyping it. This entails the following: + * - Remove all type parameters in class and method definitions. + * - Remove all abstract and alias type definitions. + * - Remove all type applications other than those involving a type test or cast. + * - Remove all empty trees in statements and definitions in a PackageDef. + * - Check that there are no double definitions in a template. + * - Add bridge definitions to a template. + * - Replace all types in type nodes and the EmptyTree object by their erasure. + * Type nodes of type Unit representing result types of methods are left alone. + * - Reset all other type attributes to `null, thus enforcing a retyping. + */ + private val preTransformer = new Transformer { + override def transform(tree: Tree): Tree = { + if (tree.symbol == ArrayClass) return tree; + val tree1 = tree match { + case ClassDef(mods, name, tparams, tpt, impl) => + if (settings.debug.value) log("defs of " + tree.symbol + " = " + tree.symbol.info.decls); + copy.ClassDef(tree, mods, name, List(), tpt, impl) + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + copy.DefDef(tree, mods, name, List(), vparamss, tpt, rhs) + case AbsTypeDef(_, _, _, _) => + EmptyTree + case AliasTypeDef(_, _, _, _) => + EmptyTree + case TypeApply(fun, args) if (fun.symbol.owner != AnyClass) => + // leave all other type tests/type casts, remove all other type applications + fun + case Template(parents, body) => + assert(!currentOwner.isImplClass); + //System.out.println("checking no dble defs " + tree);//DEBUG + checkNoDoubleDefs(tree.symbol.owner); + copy.Template(tree, parents, addBridges(body, currentOwner)); + case _ => + tree + } + tree1 match { + case EmptyTree | TypeTree() => + tree1 setType erasure(tree1.tpe) + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + val result = super.transform(tree1) setType null; + tpt.tpe = erasure(tree.symbol.tpe).resultType; + result + case _ => + super.transform(tree1) setType null + } + } + } + + /** The main transform function: Pretransfom the tree, and then + * re-type it at phase erasure.next. + */ + override def transform(tree: Tree): Tree = { + val tree1 = preTransformer.transform(tree); + atPhase(phase.next) { + val tree2 = traitTransformer.transform(tree1); + if (settings.debug.value) log("tree after addinterfaces: \n" + tree2); + newTyper(startContext.make( + unit, tree, startContext.owner, startContext.scope, startContext.imports)) + .typed(tree2) + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala new file mode 100644 index 0000000000..2a3fc9a19f --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -0,0 +1,330 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import symtab._; +import Flags._; +import util.ListBuffer; +import collection.mutable.HashMap; + +abstract class ExplicitOuter extends InfoTransform { + import global._; + import definitions._; + import posAssigner.atPos; + + override def phaseNewFlags: long = notPRIVATE | notPROTECTED; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "explicitouter"; + override def changesBaseClasses = false; + + protected def newTransformer(unit: CompilationUnit): Transformer = + new ExplicitOuterTransformer(unit); + + private def outerClass(clazz: Symbol): Symbol = + if (clazz.owner.isClass) clazz.owner + else outerClass(if (clazz.isClassLocalToConstructor) clazz.owner.owner else clazz.owner); + + private def isStatic(clazz: Symbol) = + clazz.isPackageClass || outerClass(clazz).isStaticOwner; + + /** The type transformation method: + * 1. Add an outer paramter to the formal parameters of a constructor or mixin constructor + * in a non-static class; + * 2. Add a mixin constructor $init$ to all traits except interfaces + * Leave all other types unchanged. + */ + def transformInfo(sym: Symbol, tp: Type): Type = tp match { + case MethodType(formals, restpe) => + //todo: needed? + if (sym.owner.isTrait && (sym hasFlag PROTECTED)) sym setFlag notPROTECTED; + if (sym.isConstructor && !isStatic(sym.owner)) + MethodType(formals ::: List(outerClass(sym.owner).toInterface.thisType), restpe) + else tp; + case ClassInfoType(parents, decls, clazz) => + var decls1 = decls; + if (!(clazz hasFlag INTERFACE)) { + if (!isStatic(clazz)) { + decls1 = new Scope(decls1.toList); + val outerAcc = clazz.newMethod(clazz.pos, nme.OUTER); + if ((clazz hasFlag TRAIT) || (decls.toList exists (.isClass))) + outerAcc.expandName(clazz); + decls1 enter ( + outerAcc setFlag (PARAMACCESSOR | ACCESSOR | STABLE) + setInfo MethodType(List(), outerClass(clazz).thisType)); + decls1 enter (clazz.newValue(clazz.pos, nme.getterToLocal(outerAcc.name)) + setFlag (LOCAL | PRIVATE | PARAMACCESSOR | (outerAcc getFlag EXPANDEDNAME)) + setInfo outerClass(clazz).thisType); + } + if (clazz.isTrait) { + decls1 = new Scope(decls1.toList); + decls1 enter makeMixinConstructor(clazz); + } + } + if (decls1 eq decls) tp else ClassInfoType(parents, decls1, clazz) + case PolyType(tparams, restp) => + val restp1 = transformInfo(sym, restp); + if (restp eq restp1) tp else PolyType(tparams, restp1) + case _ => + tp + } + + private def outerMember(tp: Type): Symbol = { + var e = tp.decls.elems; + while (e != null && !(e.sym.originalName.startsWith(nme.OUTER) && (e.sym hasFlag ACCESSOR))) + e = e.next; + assert(e != null, tp); + e.sym + } + + private def makeMixinConstructor(clazz: Symbol): Symbol = + clazz.newMethod(clazz.pos, nme.MIXIN_CONSTRUCTOR) setInfo MethodType(List(), UnitClass.tpe); + + /** A base class for transformers that maintain `outerParam' values for + * outer parameters of constructors. + * The class provides methods for referencing via outer. + */ + class OuterPathTransformer extends Transformer { + + /** The directly enclosing outer parameter, if we are in a constructor */ + protected var outerParam: Symbol = NoSymbol; + + /** The first outer selection from currently transformed tree + */ + protected def outerValue: Tree = + if (outerParam != NoSymbol) gen.Ident(outerParam) + else outerSelect(gen.This(currentOwner.enclClass)); + + /** The path + * `base'.$outer ... .$outer + * which refers to the outer instance `to' of value `base + */ + protected def outerPath(base: Tree, to: Symbol): Tree = + if (base.tpe.symbol == to) base else outerPath(outerSelect(base), to); + + /** Select and apply outer accessor from `base' + */ + private def outerSelect(base: Tree): Tree = { + val otp = outerClass(base.tpe.symbol).thisType; + Apply( + Select(base, outerMember(base.tpe)) setType MethodType(List(), otp), + List()) setType otp + } + + override def transform(tree: Tree): Tree = { + try {//debug + val savedOuterParam = outerParam; + tree match { + case Template(_, _) => + outerParam = NoSymbol; + case DefDef(_, _, _, vparamss, _, _) => + if (tree.symbol.isConstructor && !(isStatic(tree.symbol.owner))) { + val lastParam = vparamss.head.last; + assert(lastParam.name.startsWith(nme.OUTER), tree); + outerParam = lastParam.symbol + } + case _ => + } + val result = super.transform(tree); + outerParam = savedOuterParam; + result + } catch {//debug + case ex: Throwable => + System.out.println("exception when transforming " + tree); + throw ex + } + } + } + + class ExplicitOuterTransformer(unit: CompilationUnit) extends Transformer { + + /** The first step performs the following transformations: + * 1. A class which is not an interface and is not static gets an outer link + * (@see outerDefs) + * 2. A mixin which is not also an interface gets a mixin constructor + * (@see mixinConstructorDef) + * 3. Constructor bodies are augmented by calls to supermixin constructors + * (@see addMixinConstructorCalls) + * 4. A constructor of a class with an outer link gets an outer parameter. + * 5. A reference C.this where C refers to an outer class is replaced by a selection + * this.$outer ... .$outer (@see outerPath) + * 7. A call to a constructor Q.<init>(args) or Q.$init$(args) where Q != this and + * the constructor belongs to a non-static class is augmented by an outer argument. + * E.g. Q.<init>(args, OUTER) where OUTER is the qualifier corresponding to the + * singleton type Q. + * 8. A call to a constructor this.<init>(args) in a secondary constructor + * is augmented to this.<init>(args, OUTER) where OUTER is the last parameter + * of the secondary constructor. + */ + private val firstTransformer = new OuterPathTransformer { + + var localTyper: analyzer.Typer = typer; + + /** The two definitions + * val outer : C.this.type _; + * def outer(): C.this.type = outer ; + * Here, C is the class enclosing the class `clazz' containing the two definitions. + */ + def outerDefs(clazz: Symbol): List[Tree] = { + val outerDef = outerMember(clazz.info); + val outerVal = outerDef.accessed; + List( + localTyper.typed { + atPos(clazz.pos) { + ValDef(outerVal) + } + }, + localTyper.typed { + atPos(clazz.pos) { + DefDef(outerDef, vparamss => Select(This(clazz), outerVal)) + } + }) + } + + /** The mixin constructor definition + * def $init$(): Unit = () + */ + def mixinConstructorDef(clazz: Symbol): Tree = + localTyper.typed { + val constr = clazz.primaryConstructor; + atPhase(currentRun.explicitOuterPhase) { + // necessary so that we do not include an outer parameter already here; + // this will be added later in transform. + DefDef(constr, vparamss => Literal(())) + } + } + + /** Add calls to supermixin constructors + * super[mix].$init$() + * to `tree'. `tree' which is assumed to be the body of a constructor of class `clazz'. + */ + def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = { + def mixinConstructorCall(mixin: Symbol): Tree = + atPos(tree.pos) { + Apply( + localTyper.typedOperator { + Select(Super(clazz, mixin.name), mixin.primaryConstructor) + }, + List()) setType UnitClass.tpe; // don't type this with typed(...), + // as constructor arguments might be missing + } + val mixinConstructorCalls = + for (val mixin <- clazz.info.parents.tail; !(mixin.symbol hasFlag INTERFACE)) yield + mixinConstructorCall(mixin.symbol); + tree match { + case Block(supercall :: stats, expr) => + assert(supercall match { + case Apply(Select(Super(_, _), _), _) => true + case _ => false + }); + copy.Block(tree, supercall :: mixinConstructorCalls ::: stats, expr); + case Block(_, _) => + assert(false, tree); tree + case expr => + Block(mixinConstructorCalls, expr) setType expr.tpe setPos expr.pos; + } + } + + /** The first-step transformation method */ + override def transform(tree: Tree): Tree = { + val sym = tree.symbol; + val tree1 = tree match { + case Template(parents, decls) => + val savedLocalTyper = localTyper; + localTyper = localTyper.atOwner(tree, currentOwner); + var decls1 = decls; + if (!(currentOwner hasFlag INTERFACE)) { + if (!isStatic(currentOwner)) + decls1 = decls1 ::: outerDefs(currentOwner); // (1) + if (currentOwner.isTrait) + decls1 = decls1 ::: List(mixinConstructorDef(currentOwner)) // (2) + } + localTyper = savedLocalTyper; + copy.Template(tree, parents, decls1); + case constrDef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) + if (sym.isConstructor) => + val vparamss1 = + if (isStatic(sym.owner)) vparamss + else { // (4) + val outerField = outerMember(sym.owner.info).accessed; + val outerParam = sym.newValueParameter(sym.pos, nme.OUTER) setInfo outerField.info; + List(vparamss.head ::: List(ValDef(outerParam) setType NoType)) + } + val rhs1 = + if ((sym.isPrimaryConstructor || sym.isMixinConstructor) && sym.owner != ArrayClass) + addMixinConstructorCalls(rhs, sym.owner); // (3) + else rhs; + copy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs1); + case This(qual) => + if (sym == currentOwner.enclClass || (sym hasFlag MODULE) && sym.isStatic) tree + else atPos(tree.pos)(outerPath(outerValue, sym)); // (5) + case Apply(sel @ Select(qual, name), args) + if ((name == nme.CONSTRUCTOR || name == nme.MIXIN_CONSTRUCTOR) && !isStatic(sel.symbol.owner)) => + val outerVal = + atPos(tree.pos) { + if (qual.isInstanceOf[This]) { assert(outerParam != NoSymbol); outerValue } // (8) + else { + var pre = qual.tpe.prefix; + if (pre == NoPrefix) pre = outerClass(sym.owner).thisType; + gen.mkQualifier(pre) + } + } + copy.Apply(tree, sel, args ::: List(outerVal)) + case _ => + tree + } + super.transform(tree1) + } + } + + /** The second step performs the following transformations: + * 2. Remove private modifiers from members M of mixins T. (@see makeNotPrivate) + * 3. Remove `private' modifier from class members M that are accessed from an inner class. + * 4. Remove `protected' modifier from class members M that are accessed + * without a super qualifier accessed from an inner class. + * 5. Remove `private' and `protected' modifiers from type symbols + */ + private val secondTransformer = new Transformer { + + /** The second-step transformation method */ + override def transform(tree: Tree): Tree = { + val sym = tree.symbol; + val tree1 = super.transform(tree); + tree1 match { + case DefDef(_, _, _, _, _, _) => + if (sym.owner.isTrait && (sym hasFlag (ACCESSOR | SUPERACCESSOR))) + sym.makeNotPrivate(sym.owner); //(2) + tree1 + case Select(qual, name) => + if (currentOwner.enclClass != sym.owner) // (3) + sym.makeNotPrivate(sym.owner); + if ((sym hasFlag PROTECTED) && //(4) + !(qual.isInstanceOf[Super] || + (qual.tpe.widen.symbol isSubClass currentOwner.enclClass))) + sym setFlag notPROTECTED; + tree1 + case _ => + if (sym != null && sym.isType) {//(5) + if (sym hasFlag PRIVATE) sym setFlag notPRIVATE; + if (sym hasFlag PROTECTED) sym setFlag notPROTECTED; + } + tree1 + } + } + } + + /** The main transformation method: + * First, perform step 1 on whole tree of compilation unit. + * Then, perform step 2 on resulting tree + */ + override def transform(tree: Tree) = + atPhase(phase.next) { + secondTransformer.transform(firstTransformer.transform(tree)) + } + } +} + + diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala new file mode 100644 index 0000000000..031f7a0fcd --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -0,0 +1,112 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import symtab._; +import Flags._; +import util.ListBuffer; +import collection.mutable.HashMap; + +abstract class Flatten extends InfoTransform { + import global._; + import definitions._; + import posAssigner.atPos; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "flatten"; + + private def liftClass(sym: Symbol): unit = + if (!(sym hasFlag LIFTED)) { + sym setFlag LIFTED; + atPhase(phase.next) { + if (settings.debug.value) log("re-enter " + sym + " in " + sym.owner); + assert(sym.owner.isPackageClass, sym);//debug + val scope = sym.owner.info.decls; + val old = scope lookup sym.name; + if (old != NoSymbol) scope unlink old; + scope enter sym; + } + } + + private val flattened = new TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) if (pre.symbol.isClass && !pre.symbol.isPackageClass) => + assert(args.isEmpty); + typeRef(sym.toplevelClass.owner.thisType, sym, args) + case ClassInfoType(parents, decls, clazz) => + var parents1 = parents; + val decls1 = new Scope(); + if (clazz.isPackageClass) { + atPhase(phase.next)(decls.toList foreach (decls1 enter)); + } else { + val oldowner = clazz.owner; + atPhase(phase.next)(oldowner.info); + parents1 = List.mapConserve(parents)(this); + for (val sym <- decls.toList) { + if (sym.isTerm && !sym.isStaticModule) { + decls1 enter sym; + if (sym.isModule) sym.moduleClass setFlag LIFTED; + } else if (sym.isClass) { + liftClass(sym); + if (sym.needsImplClass) liftClass(erasure.implClass(sym)) + } + } + } + ClassInfoType(parents1, decls1, clazz) + case PolyType(tparams, restp) => + val restp1 = apply(restp); + if (restp1 eq restp) tp else PolyType(tparams, restp1) + case _ => + mapOver(tp) + } + } + + def transformInfo(sym: Symbol, tp: Type): Type = flattened(tp); + + protected def newTransformer(unit: CompilationUnit): Transformer = new Flattener; + + class Flattener extends Transformer { + + /** Buffers for lifted out classes */ + private val liftedDefs = new HashMap[Symbol, ListBuffer[Tree]]; + + override def transform(tree: Tree): Tree = { + tree match { + case PackageDef(_, _) => + liftedDefs(tree.symbol.moduleClass) = new ListBuffer; + case _ => + } + postTransform(super.transform(tree)) + } + + private def postTransform(tree: Tree): Tree = { + val sym = tree.symbol; + val tree1 = tree match { + case ClassDef(_, _, _, _, _) if sym.isNestedClass => + liftedDefs(sym.toplevelClass.owner) += tree; + EmptyTree + case Select(qual, name) if (sym.isStaticModule && !sym.owner.isPackageClass) => + atPhase(phase.next) { + atPos(tree.pos) { + gen.mkRef(sym) + } + } + case _ => + tree + } + tree1 setType flattened(tree1.tpe) + } + + /** Transform statements and add lifted definitions to them. */ + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { + val stats1 = super.transformStats(stats, exprOwner); + if (currentOwner.isPackageClass && liftedDefs(currentOwner).hasNext) + stats1 ::: liftedDefs(currentOwner).toList + else + stats1 + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/InfoTransform.scala b/src/compiler/scala/tools/nsc/transform/InfoTransform.scala new file mode 100644 index 0000000000..a5af6beb06 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/InfoTransform.scala @@ -0,0 +1,31 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +/** A base class for transforms. + * A transform contains a compiler phase which applies a tree transformer. + */ +abstract class InfoTransform extends Transform { + import global.{Symbol, Type, InfoTransformer, infoTransformers}; + + def transformInfo(sym: Symbol, tpe: Type): Type; + + override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = new Phase(prev); + + protected def changesBaseClasses = true; + + class Phase(prev: scala.tools.nsc.Phase) extends super.Phase(prev) { + if (infoTransformers.nextFrom(id).pid != id) { + val infoTransformer = new InfoTransformer { + val pid = id; + val changesBaseClasses = InfoTransform.this.changesBaseClasses; + def transform(sym: Symbol, tpe: Type): Type = transformInfo(sym, tpe); + } + infoTransformers.insert(infoTransformer) + } + } +} + diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala new file mode 100644 index 0000000000..753fc65b48 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -0,0 +1,352 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import symtab._; +import Flags._; +import util.{ListBuffer, TreeSet}; +import collection.mutable.HashMap; + +abstract class LambdaLift extends InfoTransform { + import global._; + import definitions._; + import typer.{typed, typedOperator}; + import posAssigner.atPos; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "lambdalift"; + + private val lifted = new TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + if (pre == NoPrefix && sym.isClass && !sym.isPackageClass) { + assert(args.isEmpty); + typeRef(apply(sym.owner.enclClass.thisType), sym, args) + } else mapOver(tp) + case ClassInfoType(parents, decls, clazz) => + val parents1 = List.mapConserve(parents)(this); + if (parents1 eq parents) tp + else ClassInfoType(parents1, decls, clazz) + case _ => + mapOver(tp) + } + } + + def transformInfo(sym: Symbol, tp: Type): Type = lifted(tp); + + protected def newTransformer(unit: CompilationUnit): Transformer = + new LambdaLifter(unit); + + class LambdaLifter(unit: CompilationUnit) extends explicitOuter.OuterPathTransformer { + + /** A map storing free variables of functions and classes */ + private val free = new HashMap[Symbol,SymSet]; + + /** A map storing the free variable proxies of functions and classes */ + private val proxies = new HashMap[Symbol, List[Symbol]]; + + /** A hashtable storing calls between functions */ + private val called = new HashMap[Symbol, SymSet]; + + /** The set of symbols that need to be renamed. */ + private val renamable = newSymSet; + + /** A flag to indicate whether new free variables have been found */ + private var changedFreeVars: boolean = _; + + /** Buffers for lifted out classes and methods */ + private val liftedDefs = new HashMap[Symbol, ListBuffer[Tree]]; + + private type SymSet = TreeSet[Symbol]; + + private def newSymSet = new TreeSet[Symbol]((x, y) => x.isLess(y)); + + private def symSet(f: HashMap[Symbol, SymSet], sym: Symbol): SymSet = f.get(sym) match { + case Some(ss) => ss + case None => val ss = newSymSet; f(sym) = ss; ss + } + + private def outer(sym: Symbol): Symbol = + if (sym.isConstructor) sym.owner.owner else sym.owner; + + private def enclMethOrClass(sym: Symbol): Symbol = { + def localToConstr(sym: Symbol) = + if (sym.isLocalDummy) sym.owner.primaryConstructor else sym; + var encl = localToConstr(sym); + while (!encl.isMethod && !encl.isClass) + encl = localToConstr(outer(encl)); + encl + } + + /** Mark symbol `sym' as being free in `owner', unless `sym' + * is defined in `owner' or there is a class between `owner's owner + * and the owner of `sym'. + * Return `true' if there is no class between `owner' and + * the owner of sym. + * pre: sym.isLocal, (owner.isMethod || owner.isClass) + */ + private def markFree(sym: Symbol, owner: Symbol): boolean = { + if (settings.debug.value) log("mark " + sym + " of " + sym.owner + " free in " + owner); + if (owner == enclMethOrClass(sym.owner)) true + else if (owner.isPackageClass || !markFree(sym, enclMethOrClass(outer(owner)))) false + else { + val ss = symSet(free, owner); + if (!(ss contains sym)) { + ss addEntry sym; + renamable addEntry sym; + changedFreeVars = true; + if (settings.debug.value) log("" + sym + " is free in " + owner); + if (sym.isVariable && !(sym hasFlag CAPTURED)) { + sym setFlag CAPTURED; + val symClass = sym.tpe.symbol; + atPhase(phase.next) { + sym updateInfo ( + if (isValueClass(symClass)) refClass(symClass).tpe else ObjectRefClass.tpe) + } + } + } + !owner.isClass + } + } + + def freeVars(sym: Symbol): Iterator[Symbol] = free.get(sym) match { + case Some(ss) => ss.elements + case None => Iterator.empty + } + + /** The traverse function */ + private val freeVarTraverser = new Traverser { + override def traverse(tree: Tree): unit = { + try { //debug + val sym = tree.symbol; + tree match { + case ClassDef(_, _, _, _, _) => + liftedDefs(tree.symbol) = new ListBuffer; + if (sym.isLocal) renamable addEntry sym; + case DefDef(_, _, _, _, _, _) => + if (sym.isLocal) { + renamable addEntry sym; + sym setFlag (PRIVATE | LOCAL | FINAL) + } else if (sym.isPrimaryConstructor) { + symSet(called, sym) addEntry sym.owner + } + case Ident(name) => + if (sym == NoSymbol) { + assert(name == nme.WILDCARD) + } else if (sym.isLocal) { + val owner = enclMethOrClass(currentOwner); + if (sym.isTerm && !sym.isMethod) markFree(sym, owner) + else if (owner.isMethod && sym.isMethod) symSet(called, owner) addEntry sym; + } + case Select(_, _) => + if (sym.isConstructor && sym.owner.isLocal) { + val owner = enclMethOrClass(currentOwner); + if (owner.isMethod) symSet(called, owner) addEntry sym; + } + case _ => + } + super.traverse(tree) + } catch {//debug + case ex: Throwable => + System.out.println("exception when traversing " + tree); + throw ex + } + } + } + + /** Compute free variables map `fvs'. + * Also assign unique names to all + * value/variable/let that are free in some function or class, and to + * all class/function symbols that are owned by some function. + */ + private def computeFreeVars: unit = { + freeVarTraverser.traverse(unit.body); + + do { + changedFreeVars = false; + for (val caller <- called.keys; + val callee <- called(caller).elements; + val fv <- freeVars(callee)) + markFree(fv, caller); + } while (changedFreeVars); + + for (val sym <- renamable.elements) { + sym.name = unit.fresh.newName(sym.name.toString()); + if (settings.debug.value) log("renamed: " + sym.name); + } + + atPhase(phase.next) { + for (val owner <- free.keys) { + if (settings.debug.value) + log("free(" + owner + owner.locationString + ") = " + free(owner).elements.toList); + proxies(owner) = + for (val fv <- free(owner).elements.toList) yield { + val proxy = owner.newValue(owner.pos, fv.name) + .setFlag(if (owner.isClass) PARAMACCESSOR | PRIVATE | LOCAL else PARAM) + .setFlag(SYNTHETIC) + .setInfo(fv.info); + if (owner.isClass) owner.info.decls enter proxy; + proxy + } + } + } + } + + private def proxy(sym: Symbol) = { + def searchIn(owner: Symbol): Symbol = { + if (settings.debug.value) log("searching for " + sym + "(" + sym.owner + ") in " + owner + " " + enclMethOrClass(owner));//debug + proxies.get(enclMethOrClass(owner)) match { + case Some(ps) => + ps filter (p => p.name == sym.name) match { + case List(p) => p + case List() => searchIn(outer(owner)) + } + case None => searchIn(outer(owner)) + } + } + if (settings.debug.value) log("proxy " + sym + " in " + sym.owner + " from " + currentOwner.ownerChain + " " + enclMethOrClass(sym.owner));//debug + if (enclMethOrClass(sym.owner) == enclMethOrClass(currentOwner)) sym + else searchIn(currentOwner) + } + + private def memberRef(sym: Symbol) = { + val clazz = sym.owner.enclClass; + val qual = if (clazz == currentOwner.enclClass) gen.This(clazz) + else { + sym resetFlag(LOCAL | PRIVATE); + if (clazz.isStaticOwner) gen.mkQualifier(clazz.thisType) + else outerPath(outerValue, clazz) + } + Select(qual, sym) setType sym.tpe + } + + private def proxyRef(sym: Symbol) = { + if (sym.owner.isLabel) // + gen.Ident(sym) // bq: account for the fact that LambdaLift does not know how to handle references to LabelDef parameters. + else { // + val psym = proxy(sym); + if (psym.isLocal) gen.Ident(psym) else memberRef(psym) + } + } + + private def addFreeArgs(pos: int, sym: Symbol, args: List[Tree]) = { + def freeArg(fv: Symbol) = atPos(pos)(proxyRef(fv)); + val fvs = freeVars(sym).toList; + if (fvs.isEmpty) args else args ::: (fvs map freeArg) + } + + private def addFreeParams(tree: Tree, sym: Symbol): Tree = proxies.get(sym) match { + case Some(ps) => + val freeParams = ps map (p => ValDef(p) setPos tree.pos setType NoType); + tree match { + case DefDef(mods, name, tparams, List(vparams), tpt, rhs) => + sym.updateInfo( + lifted(MethodType(sym.info.paramTypes ::: (ps map (.tpe)), sym.info.resultType))); + copy.DefDef(tree, mods, name, tparams, List(vparams ::: freeParams), tpt, rhs) + case ClassDef(mods, name, tparams, tpt, impl @ Template(parents, body)) => + copy.ClassDef(tree, mods, name, tparams, tpt, + copy.Template(impl, parents, body ::: freeParams)) + } + case None => + tree + } + + private def liftDef(tree: Tree): Tree = { + val sym = tree.symbol; + sym.owner = sym.owner.enclClass; + if (sym.isClass) sym.owner = sym.owner.toInterface; + if (sym.isMethod) sym setFlag LIFTED; + liftedDefs(sym.owner) += tree; + sym.owner.info.decls enterUnique sym; + if (settings.debug.value) log("lifted: " + sym + sym.locationString); + EmptyTree + } + + private def postTransform(tree: Tree): Tree = { + val sym = tree.symbol; + tree match { + case ClassDef(_, _, _, _, _) => + val tree1 = addFreeParams(tree, sym); + if (sym.isLocal) liftDef(tree1) else tree1 + case DefDef(_, _, _, _, _, _) => + val tree1 = addFreeParams(tree, sym); + if (sym.isLocal) liftDef(tree1) else tree1 + case ValDef(mods, name, tpt, rhs) => + if (sym.isCapturedVariable) { + val tpt1 = TypeTree(sym.tpe) setPos tpt.pos; + val rhs1 = + atPos(rhs.pos) { + typed { + Apply(Select(New(TypeTree(sym.tpe)), nme.CONSTRUCTOR), List(rhs)) + } + } + copy.ValDef(tree, mods, name, tpt1, rhs1) + } else tree + case Return(Block(stats, value)) => + Block(stats, copy.Return(tree, value)) setType tree.tpe setPos tree.pos; + case Return(expr) => + if (sym != currentOwner.enclMethod) { + System.out.println(sym);//debug + System.out.println(currentOwner.enclMethod);//debug + unit.error(tree.pos, "non-local return not yet implemented"); + } + tree + case Apply(fn, args) => + copy.Apply(tree, fn, addFreeArgs(tree.pos, sym, args)); + case Assign(Apply(TypeApply(sel @ Select(qual, _), _), List()), rhs) => + // eliminate casts introduced by selecting a captured variable field + // on the lhs of an assignment. + assert(sel.symbol == Object_asInstanceOf); + copy.Assign(tree, qual, rhs) + case Ident(name) => + val tree1 = + if (sym != NoSymbol && sym.isTerm && !sym.isLabel) + if (sym.isMethod) + atPos(tree.pos)(memberRef(sym)) + else if (sym.isLocal && enclMethOrClass(sym.owner) != enclMethOrClass(currentOwner)) + atPos(tree.pos)(proxyRef(sym)) + else tree + else tree; + if (sym.isCapturedVariable) + atPos(tree.pos) { + val tp = tree.tpe; + val elemTree = typed { Select(tree1 setType sym.tpe, nme.elem) } + if (elemTree.tpe.symbol != tp.symbol) gen.cast(elemTree, tp) else elemTree + } + else tree1 + case _ => + tree + } + } + + override def transform(tree: Tree): Tree = { + postTransform(super.transform(tree) setType lifted(tree.tpe)); + } + /** Transform statements and add lifted definitions to them. */ + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { + def addLifted(stat: Tree): Tree = stat match { + case ClassDef(mods, name, tparams, tpt, impl @ Template(parents, body)) => + val result = + if (liftedDefs(stat.symbol).hasNext) { + val lifted = liftedDefs(stat.symbol).toList map addLifted; + copy.ClassDef(stat, mods, name, tparams, tpt, + copy.Template(impl, parents, body ::: lifted)) + } else stat; + liftedDefs -= stat.symbol; + result + case _ => + stat + } + super.transformStats(stats, exprOwner) map addLifted + } + + override def transformUnit(unit: CompilationUnit): unit = { + computeFreeVars; + atPhase(phase.next)(super.transformUnit(unit)); + assert(liftedDefs.size == 0, liftedDefs.keys.toList) + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala new file mode 100644 index 0000000000..d20deb8b85 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -0,0 +1,412 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import symtab._; +import Flags._; +import util.{ListBuffer} +import scala.tools.nsc.util.Position; + +abstract class Mixin extends InfoTransform { + import global._; + import definitions._; + import posAssigner.atPos; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "mixin"; + + override def phaseNewFlags: long = lateMODULE | notABSTRACT; + + private def isForwarded(sym: Symbol) = ( + sym.owner.isImplClass && sym.isMethod && + !sym.isModule && !(sym hasFlag (ACCESSOR | SUPERACCESSOR)) + ); + + private def isStatic(sym: Symbol) = isForwarded(sym) && sym.isImplOnly; + + private def toInterface(tp: Type): Type = + atPhase(currentRun.mixinPhase)(tp.symbol.toInterface).tpe; + + private val toInterfaceMap = new TypeMap { + def apply(tp: Type): Type = mapOver( tp match { + case TypeRef(pre, sym, args) if (sym.isImplClass) => + typeRef(pre, atPhase(currentRun.mixinPhase)(sym.toInterface), args) + case _ => tp + }) + } + + private def rebindSuper(base: Symbol, member: Symbol, prevowner: Symbol): Symbol = + atPhase(currentRun.refchecksPhase) { + var bcs = base.info.baseClasses.dropWhile(prevowner !=).tail; + assert(!bcs.isEmpty/*, "" + prevowner + " " + base.info.baseClasses*/);//DEBUG + var sym: Symbol = NoSymbol; + if (settings.debug.value) log("starting rebindsuper " + base + " " + member + ":" + member.tpe + " " + prevowner + " " + base.info.baseClasses); + while (!bcs.isEmpty && sym == NoSymbol) { + if (settings.debug.value) { + val other = bcs.head.info.nonPrivateDecl(member.name); + log("rebindsuper " + bcs.head + " " + other + " " + other.tpe + " " + other.hasFlag(DEFERRED)); + } + sym = member.overridingSymbol(bcs.head).suchThat(sym => !sym.hasFlag(DEFERRED)); + bcs = bcs.tail + } + assert(sym != NoSymbol, member); + sym + } + + private def implClass(iface: Symbol): Symbol = erasure.implClass(iface); + + def addMember(clazz: Symbol, member: Symbol): Symbol = { + if (settings.debug.value) log("new member of " + clazz + ":" + member.defString);//debug + clazz.info.decls enter member; + member setFlag MIXEDIN + } + + def addLateInterfaceMembers(clazz: Symbol) = + if (!(clazz hasFlag MIXEDIN)) { + clazz setFlag MIXEDIN; + def newGetter(field: Symbol): Symbol = + clazz.newMethod(field.pos, nme.getterName(field.name)) + .setFlag(field.flags & ~(PRIVATE | LOCAL) | ACCESSOR | DEFERRED | SYNTHETIC) + .setInfo(MethodType(List(), field.info)); + def newSetter(field: Symbol): Symbol = + clazz.newMethod(field.pos, nme.getterToSetter(nme.getterName(field.name))) + .setFlag(field.flags & ~(PRIVATE | LOCAL) | ACCESSOR | DEFERRED | SYNTHETIC) + .setInfo(MethodType(List(field.info), UnitClass.tpe)); + clazz.info; + val impl = implClass(clazz); + assert(impl != NoSymbol); + for (val member <- impl.info.decls.toList) { + if (!member.isMethod && !member.isModule && !member.isModuleVar) { + assert(member.isTerm && !member.hasFlag(DEFERRED), member); + if (member.getter(impl) hasFlag PRIVATE) member.makeNotPrivate(clazz); + var getter = member.getter(clazz); + if (getter == NoSymbol) getter = addMember(clazz, newGetter(member)); + else getter setFlag (member getFlag MUTABLE); + if (!member.tpe.isInstanceOf[ConstantType]) { + var setter = member.setter(clazz); + if (setter == NoSymbol) setter = addMember(clazz, newSetter(member)); + } +/* + } else if ((member hasFlag BRIDGE) && !(member hasFlag PRIVATE)) { + member.expandName(clazz); + addMember(clazz, member.cloneSymbol(clazz) resetFlag FINAL); +*/ + } + } + if (settings.debug.value) log("new defs of " + clazz + " = " + clazz.info.decls); + } + + def addMixedinMembers(clazz: Symbol): unit = { + if (!(clazz hasFlag MIXEDIN) && (clazz != ObjectClass)) { + assert(!clazz.isTrait, clazz); + clazz setFlag MIXEDIN; + assert(!clazz.info.parents.isEmpty, clazz); + val superclazz = clazz.info.parents.head.symbol; + addMixedinMembers(superclazz); + //System.out.println("adding members of " + clazz.info.baseClasses.tail.takeWhile(superclazz !=) + " to " + clazz);//DEBUG + val mixins = clazz.info.baseClasses.tail.takeWhile(superclazz !=); + def mixinMembers(mixin: Symbol, mmap: Symbol => Symbol): unit = { + if (mixin.isImplClass) { + addLateInterfaceMembers(mixin.toInterface); + for (val member <- mixin.info.decls.toList) { + //System.out.println("adding forwarded method " + member + " " + mmap(member) + member.locationString + " to " + clazz + " " + clazz.info.member(member.name).alternatives);//DEBUG + if (isForwarded(member) && !isStatic(member) && + (clazz.info.findMember(member.name, 0, 0).alternatives contains mmap(member))) { + val member1 = addMember( + clazz, + member.cloneSymbol(clazz) setPos clazz.pos resetFlag (DEFERRED | lateDEFERRED)); + member1.asInstanceOf[TermSymbol] setAlias member; + } + } + } else if (mixin.hasFlag(lateINTERFACE)) { + addLateInterfaceMembers(mixin); + val impl = implClass(mixin); + //System.out.println("late impl " + mixin + " " + impl);//DEBUG + if (!(mixins contains impl)) mixinMembers(impl, .overriddenSymbol(mixin)); + for (val member <- mixin.info.decls.toList) { + if (member hasFlag ACCESSOR) { + val member1 = addMember( + clazz, + member.cloneSymbol(clazz) + setPos clazz.pos + setFlag FINAL resetFlag (DEFERRED | lateDEFERRED)); + if (!member.isSetter) + member.tpe match { + case MethodType(List(), ConstantType(_)) => + ; + case _ => + addMember(clazz, + clazz.newValue(member.pos, nme.getterToLocal(member.name)) + setFlag (LOCAL | PRIVATE | member.getFlag(MUTABLE)) + setInfo member.tpe.resultType) + } + } else if (member hasFlag SUPERACCESSOR) { + val member1 = addMember(clazz, member.cloneSymbol(clazz)) setPos clazz.pos; + assert(member1.alias != NoSymbol, member1); + val alias1 = rebindSuper(clazz, member.alias, mixin); + member1.asInstanceOf[TermSymbol] setAlias alias1; + } else if (member.isMethod && member.isModule && !(member hasFlag (LIFTED | BRIDGE))) { + addMember(clazz, member.cloneSymbol(clazz)) + .setPos(clazz.pos) + .resetFlag(DEFERRED | lateDEFERRED) + } + } + } + } +// for (val mixin <- mixins) if (mixin.hasFlag(lateINTERFACE)) addLateInterfaceMembers(mixin); + for (val mixin <- mixins) mixinMembers(mixin, identity); + if (settings.debug.value) log("new defs of " + clazz + " = " + clazz.info.decls); + } + } + + override def transformInfo(sym: Symbol, tp: Type): Type = tp match { + case ClassInfoType(parents, decls, clazz) => + assert(clazz.info eq tp, tp); + assert(sym == clazz, tp); + var parents1 = parents; + var decls1 = decls; + if (!clazz.isPackageClass) { + atPhase(phase.next)(clazz.owner.info); + if (clazz.isImplClass) { + clazz setFlag lateMODULE; + var sourceModule = clazz.owner.info.decls.lookup(sym.name.toTermName); + if (sourceModule != NoSymbol) { + sourceModule setPos sym.pos; + sourceModule.flags = MODULE | FINAL; + } else { + sourceModule = clazz.owner.newModule( + sym.pos, sym.name.toTermName, sym.asInstanceOf[ClassSymbol]); + clazz.owner.info.decls enter sourceModule + } + sourceModule setInfo sym.tpe; + assert(clazz.sourceModule != NoSymbol);//debug + parents1 = List(); + decls1 = new Scope(decls.toList filter isForwarded) + } else if (!parents.isEmpty) { + parents1 = parents.head :: (parents.tail map toInterface); + } + } + //decls1 = atPhase(phase.next)(new Scope(decls1.toList));//debug + if ((parents1 eq parents) && (decls1 eq decls)) tp + else ClassInfoType(parents1, decls1, clazz); + + case MethodType(formals, restp) => + toInterfaceMap( + if (isForwarded(sym)) MethodType(toInterface(sym.owner.typeOfThis) :: formals, restp) + else tp) + + case _ => + tp + } + + protected def newTransformer(unit: CompilationUnit): Transformer = new MixinTransformer; + + class MixinTransformer extends Transformer { + private var self: Symbol = _; + private val rootContext = erasure.NoContext.make(EmptyTree, RootClass, new Scope()); + private var localTyper: erasure.Typer = _; + private var enclInterface: Symbol = _; + + private def preTransform(tree: Tree): Tree = { + val sym = tree.symbol; + tree match { + case Template(parents, body) => + localTyper = erasure.newTyper(rootContext.make(tree, currentOwner)); + atPhase(phase.next)(currentOwner.owner.info);//needed? + if (!currentOwner.isTrait) addMixedinMembers(currentOwner) + else if (currentOwner hasFlag lateINTERFACE) addLateInterfaceMembers(currentOwner); + tree + case DefDef(mods, name, tparams, List(vparams), tpt, rhs) if currentOwner.isImplClass => + if (isForwarded(sym)) { + sym setFlag notOVERRIDE; + self = sym.newValue(sym.pos, nme.SELF) + .setFlag(PARAM | SYNTHETIC) + .setInfo(toInterface(currentOwner.typeOfThis)); + enclInterface = currentOwner.toInterface; + val selfdef = ValDef(self) setType NoType; + copy.DefDef(tree, mods, name, tparams, List(selfdef :: vparams), tpt, rhs) + } else { + EmptyTree + } + case Apply(tapp @ TypeApply(fn, List(arg)), List()) => + if (arg.tpe.symbol.isImplClass) { + val ifacetpe = toInterface(arg.tpe); + arg.tpe = ifacetpe; + tapp.tpe = MethodType(List(), ifacetpe); + tree.tpe = ifacetpe + } + tree + case ValDef(_, _, _, _) if (currentOwner.isImplClass) => + EmptyTree + case _ => + tree + } + } + + private def selfRef(pos: int) = gen.Ident(self) setPos pos; + + private def staticRef(sym: Symbol) = { + sym.owner.info; + sym.owner.owner.info; + if (sym.owner.sourceModule == NoSymbol) { + assert(false, "" + sym + " in " + sym.owner + " in " + sym.owner.owner + " " + sym.owner.owner.info.decls.toList);//debug + } + Select(gen.mkRef(sym.owner.sourceModule), sym); + } + + private def addNewDefs(clazz: Symbol, stats: List[Tree]): List[Tree] = { + val newDefs = new ListBuffer[Tree]; + def addDef(pos: int, tree: Tree): unit = { + if (settings.debug.value) log("add new def to " + clazz + ": " + tree); + newDefs += localTyper.typed { + atPos(pos) { + tree + } + } + } + def position(sym: Symbol) = + if (sym.pos == Position.NOPOS) clazz.pos else sym.pos; + def addDefDef(sym: Symbol, rhs: List[Symbol] => Tree): unit = + addDef(position(sym), DefDef(sym, vparamss => rhs(vparamss.head))); + def add(stats: List[Tree], newDefs: List[Tree]) = { + val newSyms = newDefs map (.symbol); + def isNotDuplicate(tree: Tree) = tree match { + case DefDef(_, _, _, _, _, _) => + val sym = tree.symbol; + !((sym hasFlag DEFERRED) && + (newSyms exists (nsym => nsym.name == sym.name && (nsym.tpe matches sym.tpe)))) + case _ => + true + } + if (newDefs.isEmpty) stats + else stats.filter(isNotDuplicate) ::: newDefs + } + def completeSuperAccessor(stat: Tree) = stat match { + case DefDef(mods, name, tparams, List(vparams), tpt, EmptyTree) + if (stat.symbol hasFlag SUPERACCESSOR) => + val rhs0 = + Apply(Select(Super(clazz, nme.EMPTY.toTypeName), stat.symbol.alias), + vparams map (vparam => Ident(vparam.symbol))); + if (settings.debug.value) log("complete super acc " + stat.symbol + stat.symbol.locationString + " " + rhs0 + " " + stat.symbol.alias + stat.symbol.alias.locationString);//debug + val rhs1 = postTransform(localTyper.typed(atPos(stat.pos)(rhs0), stat.symbol.tpe.resultType)); + copy.DefDef(stat, mods, name, tparams, List(vparams), tpt, rhs1) + case _ => + stat + } + for (val sym <- clazz.info.decls.toList) { + if (sym hasFlag MIXEDIN) { + if (clazz hasFlag lateINTERFACE) { + addDefDef(sym, vparamss => EmptyTree) + } else if (!clazz.isTrait) { + if (sym hasFlag ACCESSOR) { + addDefDef(sym, vparams => { + val accessedRef = sym.tpe match { + case MethodType(List(), ConstantType(c)) => Literal(c) + case _ => Select(This(clazz), sym.accessed) + } + if (sym.isSetter) Assign(accessedRef, Ident(vparams.head)) else accessedRef}) + } else if (sym.isModule && !(sym hasFlag LIFTED)) { + val vdef = refchecks.newModuleVarDef(sym); + addDef(position(sym), vdef); + addDef(position(sym), refchecks.newModuleAccessDef(sym, vdef.symbol)); + } else if (!sym.isMethod) { + addDef(position(sym), ValDef(sym)) + } else if (sym hasFlag SUPERACCESSOR) { + addDefDef(sym, vparams => EmptyTree) + } else { + assert(sym.alias != NoSymbol, sym); + addDefDef(sym, vparams => + Apply(staticRef(sym.alias), gen.This(clazz) :: (vparams map Ident))) + } + } + } + } + val stats1 = add(stats, newDefs.toList); + if (clazz.isTrait) stats1 else stats1 map completeSuperAccessor; + } + + private def postTransform(tree: Tree): Tree = { + val sym = tree.symbol; + tree match { + case Template(parents, body) => + val parents1 = currentOwner.info.parents map (t => TypeTree(t) setPos tree.pos); + val body1 = addNewDefs(currentOwner, body); + copy.Template(tree, parents1, body1) + case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) + if (tree.symbol == Object_asInstanceOf && (qual.tpe <:< targ.tpe)) => + qual + case Apply(Select(qual, _), args) => + def staticCall(target: Symbol) = { + if (target == NoSymbol) + assert(false, "" + sym + " " + sym.owner + " " + implClass(sym.owner) + " " + sym.owner.owner + atPhase(phase.prev)(sym.owner.owner.info.decls.toList));//debug + localTyper.typed { + atPos(tree.pos) { + val qual1 = + if (!qual.isInstanceOf[Super]) qual + else if (currentOwner.enclClass.isImplClass) selfRef(qual.pos) + else gen.This(currentOwner.enclClass); + Apply(staticRef(target), qual1 :: args) + } + } + } + assert(sym != NoSymbol, tree);//debug + if (isStatic(sym)) { + //assert(sym.isConstructor || currentOwner.enclClass.isImplClass, tree); + staticCall(sym) + } else qual match { + case Super(_, mix) => + if (mix == nme.EMPTY.toTypeName) { + if (currentOwner.enclClass.isImplClass) + assert(false, "illegal super in mixin class: " + currentOwner.enclClass + " " + tree); + } + if (sym.owner hasFlag lateINTERFACE) + staticCall(atPhase(phase.prev)(sym.overridingSymbol(implClass(sym.owner)))) + else { + assert(!(sym.owner hasFlag INTERFACE)); + assert(!currentOwner.enclClass.isImplClass); + tree + } + case _ => + tree + } + + case This(_) if tree.symbol.isImplClass => + assert(tree.symbol == currentOwner.enclClass); + selfRef(tree.pos) + case Select(Super(_, _), name) => + tree + case Select(qual, name) if sym.owner.isImplClass && !isStatic(sym) => + assert(!sym.isMethod, sym); + val getter = sym.getter(enclInterface); + assert(getter != NoSymbol); + localTyper.typed { + atPos(tree.pos) { + Apply(Select(qual, getter), List()) + } + } + case Assign(Apply(lhs @ Select(qual, _), List()), rhs) => + localTyper.typed { + atPos(tree.pos) { + Apply(Select(qual, lhs.symbol.setter(enclInterface)) setPos lhs.pos, List(rhs)) + } + } + case _ => + tree + } + } + + override def transform(tree: Tree): Tree = { + try { //debug + val tree1 = super.transform(preTransform(tree)); + atPhase(phase.next)(postTransform(tree1)) + } catch { + case ex: Throwable => + System.out.println("exception when traversing " + tree); + throw ex + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala new file mode 100644 index 0000000000..2f80216dbe --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -0,0 +1,126 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import util.HashSet; +import collection.mutable.HashMap; +import symtab.Flags._; + +abstract class OverridingPairs { + + val global: Global; + import global._; + + class Cursor(base: Symbol) { + + private val self = base.thisType; + + protected def exclude(sym: Symbol): boolean = sym.isConstructor || (sym hasFlag LOCAL); + + protected def parents: List[Type] = base.info.parents; + + protected def matches(sym1: Symbol, sym2: Symbol): boolean = + sym1.isType || (self.memberType(sym1) matches self.memberType(sym2)); + + private type BitSet = Array[int]; + + private def newBitSet(size: int): BitSet = new Array((size + 31) >> 5); + + private def include(bs: BitSet, n: int): unit = { + val nshifted = n >> 5; + val nmask = 1 << (n & 31); + bs(nshifted) = bs(nshifted) | nmask + } + + private def intersectionContainsElementLeq(bs1: BitSet, bs2: BitSet, n: int): boolean = { + val nshifted = n >> 5; + val nmask = 1 << (n & 31); + ((List.range(0, nshifted) exists (i => (bs1(i) & bs2(i)) != 0)) || + ((bs1(nshifted) & bs2(nshifted) & (nmask | nmask - 1)) != 0)) + } + + private val decls = new Scope; + { def fillDecls(bcs: List[Symbol], deferredflag: int): unit = + if (!bcs.isEmpty) { + fillDecls(bcs.tail, deferredflag); + var e = bcs.head.info.decls.elems; + while (e != null) { + if (e.sym.getFlag(DEFERRED) == deferredflag && !exclude(e.sym)) decls enter e.sym; + e = e.next + } + } + fillDecls(base.info.baseClasses, DEFERRED); + fillDecls(base.info.baseClasses, 0); + } + + private val size = base.info.baseClasses.length; + + private val index = new HashMap[Symbol, int]; + { var i = 0; + for (val bc <- base.info.baseClasses) { + index(bc) = i; + i = i + 1 + } + } + + private val subParents = new Array[BitSet](size); + { for (val i <- List.range(0, size)) + subParents(i) = new BitSet(size); + for (val p <- parents) { + val pIndex = index(p.symbol); + for (val bc <- p.baseClasses) include(subParents(index(bc)), pIndex); + } + } + + + private def hasCommonParent(sym1: Symbol, sym2: Symbol) = { + //assert(index.get(sym1.owner) != None, "" + base + " " + sym1 + " " + sym1.owner);//DEBUG + //assert(index.get(sym2.owner) != None, "" + base + " " + sym2 + " " + sym2.owner);//DEBUG + val index1 = index(sym1.owner); + val index2 = index(sym2.owner); + val minindex = if (index1 < index2) index1 else index2; + intersectionContainsElementLeq(subParents(index1), subParents(index2), minindex) + } + + private val visited = new HashSet[ScopeEntry](256); + private var curEntry = decls.elems; + private var nextEntry = curEntry; + + var overriding: Symbol = _; + var overridden: Symbol = _; + + def hasNext: boolean = curEntry != null; + + def next: unit = + if (curEntry != null) { + overriding = curEntry.sym; + if (nextEntry != null) { + do { + nextEntry = decls.lookupNextEntry(nextEntry); + } while (nextEntry != null && + ((nextEntry.sym hasFlag PRIVATE) || + (overriding.owner == nextEntry.sym.owner) || + (hasCommonParent(overriding, nextEntry.sym)) || + (!matches(overriding, nextEntry.sym)) || + (overriding hasFlag LOCAL))) + } + if (nextEntry != null) { + overridden = nextEntry.sym; + //System.out.println("yield: " + overriding + overriding.locationString + " / " + overridden + overridden.locationString);//DEBUG + visited addEntry nextEntry + } else { + do { + curEntry = curEntry.next + } while (curEntry != null && (visited contains curEntry)); + nextEntry = curEntry; + next + } + } + + next + } +} + diff --git a/src/compiler/scala/tools/nsc/transform/SampleTransform.scala b/src/compiler/scala/tools/nsc/transform/SampleTransform.scala new file mode 100644 index 0000000000..404e03e572 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/SampleTransform.scala @@ -0,0 +1,45 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +/** A sample transform. + */ +abstract class SampleTransform extends Transform { + // inherits abstract value `global' and class `Phase' from Transform + + import global._; // the global environment + import definitions._; // standard classes and methods + import typer.{typed, atOwner}; // methods to type trees + import posAssigner.atPos; // for filling in tree positions + + /** the following two members override abstract members in Transform */ + val phaseName: String = "sample-phase"; + protected def newTransformer(unit: CompilationUnit): Transformer = new SampleTransformer(unit); + + class SampleTransformer(unit: CompilationUnit) extends Transformer { + + override def transform(tree: Tree): Tree = { + val tree1 = super.transform(tree); // transformers always maintain `currentOwner'. + tree1 match { + case Block(List(), expr) => // a simple optimization + expr + case Block(defs, sup @ Super(qual, mix)) => // A hypthothetic transformation, which replaces + // {super} by {super.sample} + copy.Block( // `copy' is the usual lazy tree copier + tree1, defs, + typed( // `typed' assigns types to its tree argument + atPos(tree1.pos)( // `atPos' fills in position of its tree argument + Select( // The `Select' factory method is defined in class `Trees' + sup, + currentOwner.newValue( // creates a new term symbol owned by `currentowner' + tree1.pos, + newTermName("sample")))))) // The standard term name creator + case _ => + tree1 + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala new file mode 100644 index 0000000000..c6fa574228 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -0,0 +1,286 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import scala.tools.nsc.symtab.Flags; + +/** Perform tail recursive call elimination. + */ +abstract class TailCalls extends Transform + /* with JavaLogging() */ { + // inherits abstract value `global' and class `Phase' from Transform + + import global._; // the global environment + import definitions._; // standard classes and methods + import typer.{typed, atOwner}; // methods to type trees + import posAssigner.atPos; // for filling in tree positions + + val phaseName: String = "tailcalls"; + def newTransformer(unit: CompilationUnit): Transformer = new TailCallElimination(unit); + + /** + * A Tail Call Transformer + * + * @author Erik Stenman, Iulian Dragos + * @version 1.1 + * + * What it does: + * + * 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. + * + * 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. + * + * A method call is self-recursive if it calls the current method on + * the current instance and the method is final (otherwise, it could + * be a call to an overridden method in a subclass). Furthermore, If + * the method has type parameters, the call must contain these + * parameters as type arguments. + * + * 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). + * + * 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. + * + * Assumes: Uncurry has been run already, and no multiple parameter + * lists exit. + */ + class TailCallElimination(unit: CompilationUnit) extends Transformer { + + class Context { + /** The current method */ + var currentMethod: Symbol = NoSymbol; + + /** The current tail-call label */ + var label: Symbol = NoSymbol; + + /** The expected type arguments of self-recursive calls */ + var tparams: List[Symbol] = Nil; + + /** Tells whether we are in a (possible) tail position */ + var tailPos = false; + + /** Is the label accessed? */ + var accessed = false; + + def this(that: Context) = { + this(); + this.currentMethod = that.currentMethod; + this.label = that.label; + this.tparams = that.tparams; + this.tailPos = that.tailPos; + this.accessed = that.accessed; + } + + /** Create a new method symbol for the current method and store it in + * the label field. + */ + def makeLabel(): Unit = { + label = currentMethod.newLabel(currentMethod.pos, "_" + currentMethod.name); + accessed = false; + } + + override def toString(): String = ( + "" + currentMethod.name + " tparams: " + tparams + " tailPos: " + tailPos + + " accessed: " + accessed + "\nLabel: " + label + "\nLabel type: " + label.info + ); + } + + private def mkContext(that: Context) = new Context(that); + private def mkContext(that: Context, tp: Boolean): Context = { + val t = mkContext(that); + t.tailPos = tp; + t + } + + private var ctx: Context = new Context(); + + /** Rewrite this tree to contain no tail recursive calls */ + def transform(tree: Tree, nctx: Context): Tree = { + val oldCtx = ctx; + ctx = nctx; + val t = transform(tree); + this.ctx = oldCtx; + t + } + + override def transform(tree: Tree): Tree = { + tree match { + + case DefDef(mods, name, tparams, vparams, tpt, rhs) => + log("Entering DefDef: " + name); + val newCtx = mkContext(ctx); + newCtx.currentMethod = tree.symbol; + newCtx.makeLabel(); + newCtx.label.setInfo(tree.symbol.info); + newCtx.tailPos = true; + + val t1 = if (newCtx.currentMethod.isFinal || + newCtx.currentMethod.enclClass.hasFlag(Flags.MODULE)) { + newCtx.tparams = Nil; + log(" Considering " + name + " for tailcalls"); + tree.symbol.tpe match { + case PolyType(tpes, restpe) => + newCtx.tparams = tparams map (.symbol); + newCtx.label.setInfo( + restpe.substSym(tpes, tparams map (.symbol))); + log("adding types: " + newCtx.tparams); + log("setting return resultType to: " + newCtx.label.tpe); + case _ => (); + } + + var newRHS = transform(rhs, newCtx); + if (newCtx.accessed) { + log("Rewrote def " + newCtx.currentMethod); + + newRHS = + typed(atPos(tree.pos)( + LabelDef(newCtx.label, + List.flatten(vparams) map (.symbol), + newRHS))); + copy.DefDef(tree, mods, name, tparams, vparams, tpt, newRHS); + } else + copy.DefDef(tree, mods, name, tparams, vparams, tpt, newRHS); + } else { + log("Non-final method: " + name); + // Martin: OK like that? + copy.DefDef(tree, mods, name, tparams, vparams, tpt, transform(rhs, newCtx)) + } + log("Leaving DefDef: " + name); + t1; + + case EmptyTree => tree; + + case PackageDef(name, stats) => super.transform(tree); + case ClassDef(mods, name, tparams, tpt, impl) => + log("Entering class " + name); + val res = super.transform(tree); + log("Leaving class " + name); + res + + case ValDef(mods, name, tpt, rhs) => tree; + case AbsTypeDef(mods, name, lo, hi) => tree; // (eliminated by erasure) + case AliasTypeDef(mods, name, tparams, rhs) => tree; // (eliminated by erasure) + case LabelDef(name, params, rhs) => super.transform(tree); + + case Template(parents, body) => super.transform(tree); + + case Block(stats, expr) => + copy.Block(tree, + transformTrees(stats, mkContext(ctx, false)), + transform(expr)); + + case CaseDef(pat, guard, body) => + copy.CaseDef(tree, pat, guard, transform(body)); + + case Sequence(_) | Alternative(_) | + Star(_) | Bind(_, _) => + throw new RuntimeException("We should've never gotten inside a pattern"); + + case Function(vparams, body) => + tree + //throw new RuntimeException("Anonymous function should not exist at this point. at: " + unit.position(tree.pos)); + + case Assign(lhs, rhs) => super.transform(tree); + case If(cond, thenp, elsep) => + copy.If(tree, cond, transform(thenp), transform(elsep)); + + case Match(selector, cases) => super.transform(tree); + case Return(expr) => super.transform(tree); + case Try(block, catches, finalizer) => super.transform(tree); + + case Throw(expr) => super.transform(tree); + case New(tpt) => super.transform(tree); + case Typed(expr, tpt) => super.transform(tree); + + case Apply(tapply @ TypeApply(fun, targs), vargs) => + if ( ctx.currentMethod.isFinal && + ctx.tailPos && + isSameTypes(ctx.tparams, targs map (.tpe.symbol)) && + isRecursiveCall(fun)) + rewriteTailCall(fun, transformTrees(vargs, mkContext(ctx, false))) + else + copy.Apply(tree, tapply, transformTrees(vargs, mkContext(ctx, false))); + + case TypeApply(fun, args) => + super.transform(tree); +// throw new RuntimeException("Lonely TypeApply found -- we can only handle them inside Apply(TypeApply()): " + tree + " at: " + unit); + + case Apply(fun, args) => + if (ctx.currentMethod.isFinal && + ctx.tailPos && + isRecursiveCall(fun)) + rewriteTailCall(fun, transformTrees(args, mkContext(ctx, false))); + else + copy.Apply(tree, fun, transformTrees(args, mkContext(ctx, false))); + + case Super(qual, mixin) => + tree; + case This(qual) => + tree; + case Select(qualifier, selector) => + tree; + case Ident(name) => + tree; + case Literal(value) => + tree; + case TypeTree() => tree; + + case _ => + tree + } + } + + def transformTrees(trees: List[Tree], nctx: Context): List[Tree] = + trees map ((tree) => transform(tree, nctx)); + + private def rewriteTailCall(fun: Tree, args: List[Tree]): Tree = { + log("Rewriting tail recursive method call at: " + + unit.position(fun.pos)); + ctx.accessed = true; + typed(atPos(fun.pos)( + Apply(Ident(ctx.label), args))); + } + + private def isSameTypes(ts1: List[Symbol], ts2: List[Symbol]): Boolean = { + def isSameType(t1: Symbol, t2: Symbol) = { + log("" + t1 + " vs " + t2); + t1 == t2 + } + List.forall2(ts1, ts2)(isSameType) + } + + /** Return true if the fun tree refers to the same method as the one + * saved in ctx. If it is a method call, we check that it is applied to + * "this" + */ + private def isRecursiveCall(fun: Tree): Boolean = + if (fun.symbol eq ctx.currentMethod) + fun match { + case Select(t @ This(_), _) => + assert(t.symbol == ctx.currentMethod.owner, "This refers to other class: " + + t.symbol + ": " + ctx.currentMethod.owner); + true; + + case Ident(_) => true; + case _ => false; + } + else + false; + } + +} diff --git a/src/compiler/scala/tools/nsc/transform/Transform.scala b/src/compiler/scala/tools/nsc/transform/Transform.scala new file mode 100644 index 0000000000..cfdf796f27 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/Transform.scala @@ -0,0 +1,26 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +/** A base class for transforms. + * A transform contains a compiler phase which applies a tree transformer. + */ +abstract class Transform extends SubComponent { + + /** The transformer factory */ + protected def newTransformer(unit: global.CompilationUnit): global.Transformer; + + /** Create a new phase which applies transformer */ + def newPhase(prev: scala.tools.nsc.Phase): StdPhase = new Phase(prev); + + /** The phase defined by this transform */ + class Phase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) { + def apply(unit: global.CompilationUnit): unit = { + newTransformer(unit).transformUnit(unit); + } + } +} + diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala new file mode 100644 index 0000000000..a435b2a2b2 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -0,0 +1,365 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.transform; + +import symtab.Flags._; + +/*<export>*/ +/** - uncurry all symbol and tree types (@see UnCurryPhase) + * - for every curried parameter list: (ps_1) ... (ps_n) ==> (ps_1, ..., ps_n) + * - for every curried application: f(args_1)...(args_n) ==> f(args_1, ..., args_n) + * - for every type application: f[Ts] ==> f[Ts]() unless followed by parameters + * - for every use of a parameterless function: f ==> f() and q.f ==> q.f() + * - for every def-parameter: x: => T ==> x: () => T + * - for every use of a def-parameter: x ==> x.apply() + * - for every argument to a def parameter `x: => T': + * if argument is not a reference to a def parameter: + * convert argument `e' to (expansion of) `() => e' + * - for every repated parameter `x: T*' --> x: Seq[a]. + * - for every argument list that corresponds to a repeated parameter + * (a_1, ..., a_n) => (Seq(a_1, ..., a_n)) + * - for every argument list that is an escaped sequence + * (a_1:_*) => (a_1)g + * - convert implicit method types to method types + * - convert non-trivial catches in try statements to matches + */ +/*</export>*/ +abstract class UnCurry extends InfoTransform { + import global._; // the global environment + import definitions._; // standard classes and methods + import typer.{typed}; // methods to type trees + import posAssigner.atPos; // for filling in tree positions + + val phaseName: String = "uncurry"; + def newTransformer(unit: CompilationUnit): Transformer = new UnCurryTransformer(unit); + override def changesBaseClasses = false; + + private val uncurry = new TypeMap { + def apply(tp: Type): Type = tp match { + case MethodType(formals, MethodType(formals1, restpe)) => + apply(MethodType(formals ::: formals1, restpe)) + case mt: ImplicitMethodType => + apply(MethodType(mt.paramTypes, mt.resultType)) + case PolyType(List(), restpe) => + apply(MethodType(List(), restpe)) + case PolyType(tparams, restpe) => + PolyType(tparams, apply(MethodType(List(), restpe))) + case TypeRef(pre, sym, List(arg)) if (sym == ByNameParamClass) => + apply(functionType(List(), arg)) + case TypeRef(pre, sym, args) if (sym == RepeatedParamClass) => + apply(rawTypeRef(pre, SeqClass, args)); + case _ => + mapOver(tp) + } + } + + /** - return symbol's transformed type, + * - if symbol is a def parameter with transformed type T, return () => T + */ + def transformInfo(sym: Symbol, tp: Type): Type = + if (sym.isType) tp + else uncurry(tp); + + class UnCurryTransformer(unit: CompilationUnit) extends Transformer { + + private var needTryLift = false; + private var inPattern = false; + private var inConstructorFlag = 0L; + + override def transform(tree: Tree): Tree = try { //debug + postTransform(mainTransform(tree)); + } catch { + case ex: Throwable => + System.out.println("exception when traversing " + tree); + throw ex + } + + /* Is tree a reference `x' to a call by name parameter that neeeds to be converted to + * x.apply()? Note that this is not the case if `x' is used as an argument to another + * call by name parameter. + */ + def isByNameRef(tree: Tree): boolean = ( + tree.isTerm && tree.hasSymbol && + tree.symbol.tpe.symbol == ByNameParamClass && tree.tpe == tree.symbol.tpe.typeArgs.head + ); + + /** Uncurry a type of a tree node. + * This function is sensitive to whether or not we are in a pattern -- when in a pattern + * additional parameter sections of a case class are skipped. + */ + def uncurryTreeType(tp: Type): Type = tp match { + case MethodType(formals, MethodType(formals1, restpe)) if (inPattern) => + uncurryTreeType(MethodType(formals, restpe)) + case _ => + uncurry(tp) + } + + /* Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to + * + * class $anon() extends Object() with FunctionN[T_1, .., T_N, R] with ScalaObject { + * def apply(x_1: T_1, ..., x_N: T_n): R = body + * } + * new $anon() + * + * transform a function node (x => body) of type PartialFunction[T, R] where + * body = x match { case P_i if G_i => E_i }_i=1..n + * to: + * + * class $anon() extends Object() with PartialFunction[T, R] with ScalaObject { + * def apply(x: T): R = body; + * def isDefinedAt(x: T): boolean = x match { + * case P_1 if G_1 => true + * ... + * case P_n if G_n => true + * case _ => false + * } + * } + * new $anon() + * + * However, if one of the patterns P_i if G_i is a default pattern, generate instead + * + * def isDefinedAt(x: T): boolean = true + */ + def transformFunction(fun: Function): Tree = { + val anonClass = fun.symbol.owner.newAnonymousFunctionClass(fun.pos) + .setFlag(FINAL | SYNTHETIC | inConstructorFlag); + val formals = fun.tpe.typeArgs.init; + val restpe = fun.tpe.typeArgs.last; + anonClass setInfo ClassInfoType( + List(ObjectClass.tpe, fun.tpe, ScalaObjectClass.tpe), new Scope(), anonClass); + val applyMethod = anonClass.newMethod(fun.pos, nme.apply) + .setFlag(FINAL).setInfo(MethodType(formals, restpe)); + anonClass.info.decls enter applyMethod; + for (val vparam <- fun.vparams) vparam.symbol.owner = applyMethod; + new ChangeOwnerTraverser(fun.symbol, applyMethod).traverse(fun.body); + var members = List( + DefDef(Modifiers(FINAL), nme.apply, List(), List(fun.vparams), TypeTree(restpe), fun.body) + setSymbol applyMethod); + if (fun.tpe.symbol == PartialFunctionClass) { + val isDefinedAtMethod = anonClass.newMethod(fun.pos, nme.isDefinedAt) + .setFlag(FINAL).setInfo(MethodType(formals, BooleanClass.tpe)); + anonClass.info.decls enter isDefinedAtMethod; + def idbody(idparam: Symbol) = fun.body match { + case Match(_, cases) => + val substParam = new TreeSymSubstituter(List(fun.vparams.head.symbol), List(idparam)); + def transformCase(cdef: CaseDef): CaseDef = + resetAttrs(CaseDef(cdef.pat.duplicate, cdef.guard.duplicate, Literal(true))); + if (cases exists treeInfo.isDefaultCase) Literal(true) + else + Match( + Ident(idparam), + (cases map transformCase) ::: + List(CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(false)))) + } + members = DefDef(isDefinedAtMethod, vparamss => idbody(vparamss.head.head)) :: members; + } + typer.atOwner(currentOwner).typed { + atPos(fun.pos) { + Block( + List(ClassDef(anonClass, List(List()), List(List()), members)), + Typed( + New(TypeTree(anonClass.tpe), List(List())), + TypeTree(fun.tpe))) + } + } + } + + def transformArgs(pos: int, args: List[Tree], formals: List[Type]) = { + if (formals.isEmpty) { + assert(args.isEmpty); List() + } else { + val args1 = + formals.last match { + case TypeRef(pre, sym, List(elempt)) if (sym == RepeatedParamClass) => + def mkSequence(args: List[Tree]) = + atPos(pos)(ArrayValue(TypeTree(elempt), args) setType formals.last); + if (args.isEmpty) List(mkSequence(args)) + else { + val suffix = args.last match { + case Typed(arg, Ident(name)) if name == nme.WILDCARD_STAR.toTypeName => + arg setType seqType(arg.tpe) + case _ => + mkSequence(args.drop(formals.length - 1)) + } + args.take(formals.length - 1) ::: List(suffix) + } + case _ => args + } + List.map2(formals, args1) ((formal, arg) => + if (formal.symbol != ByNameParamClass) arg + else if (isByNameRef(arg)) arg setType functionType(List(), arg.tpe) + else { + val fun = typer.atOwner(currentOwner).typed( + Function(List(), arg) setPos arg.pos).asInstanceOf[Function]; + new ChangeOwnerTraverser(currentOwner, fun.symbol).traverse(arg); + transformFunction(fun) + }) + } + } + + def mainTransform(tree: Tree): Tree = { + + def withNeedLift(needLift: Boolean)(f: => Tree): Tree = { + val savedNeedTryLift = needTryLift; + needTryLift = needLift; + val t = f; + needTryLift = savedNeedTryLift; + t + } + + def withInConstructorFlag(inConstructorFlag: long)(f: => Tree): Tree = { + val savedInConstructorFlag = this.inConstructorFlag; + this.inConstructorFlag = inConstructorFlag; + val t = f; + this.inConstructorFlag = savedInConstructorFlag; + t + } + + tree match { + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + withNeedLift(false) { + if (tree.symbol.isConstructor) { + atOwner(tree.symbol) { + val rhs1 = rhs match { + case Block(stat :: stats, expr) => + copy.Block( + rhs, + withInConstructorFlag(INCONSTRUCTOR) { transform(stat) } :: transformTrees(stats), + transform(expr)); + case _ => + withInConstructorFlag(INCONSTRUCTOR) { transform(rhs) } + } + copy.DefDef( + tree, mods, name, transformAbsTypeDefs(tparams), + transformValDefss(vparamss), transform(tpt), rhs1) + } + } else { + super.transform(tree) + } + } + + case ValDef(_, _, _, rhs) + if (!tree.symbol.owner.isSourceMethod) => + withNeedLift(true) { super.transform(tree) } + + + case Apply(Select(Block(List(), Function(vparams, body)), nme.apply), args) => + // perform beta-reduction; this helps keep view applications small + withNeedLift(true) { + mainTransform(new TreeSubstituter(vparams map (.symbol), args).transform(body)) + } + + case Apply(fn, args) => + if (settings.noassertions.value && + fn.symbol != null && + (fn.symbol.name == nme.assert_ || fn.symbol.name == nme.assume_) && + fn.symbol.owner == PredefModule.moduleClass) { + Literal(()).setPos(tree.pos).setType(UnitClass.tpe) + } else { + withNeedLift(true) { + val formals = fn.tpe.paramTypes; + copy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, args, formals))) + } + } + + case Assign(Select(_, _), _) => + withNeedLift(true) { super.transform(tree) } + + case Try(block, catches, finalizer) => + if (needTryLift) { + if (settings.debug.value) + log("lifting try at: " + unit.position(tree.pos)); + + val sym = currentOwner.newMethod(tree.pos, unit.fresh.newName("liftedTry")); + sym.setInfo(MethodType(List(), tree.tpe)); + new ChangeOwnerTraverser(currentOwner, sym).traverse(tree); + + transform(typed(atPos(tree.pos)( + Block(List(DefDef(sym, List(List()), tree)), + Apply(Ident(sym), Nil))))) + } else + super.transform(tree) + + case CaseDef(pat, guard, body) => + inPattern = true; + val pat1 = transform(pat); + inPattern = false; + copy.CaseDef(tree, pat1, transform(guard), transform(body)) + + case fun @ Function(_, _) => + mainTransform(transformFunction(fun)) + + case Template(_, _) => + withInConstructorFlag(0) { super.transform(tree) } + + case _ => + val tree1 = super.transform(tree); + if (isByNameRef(tree1)) + typed(atPos(tree1.pos)( + Apply(Select(tree1 setType functionType(List(), tree1.tpe), nme.apply), List()))) + else tree1; + } + } setType uncurryTreeType(tree.tpe); + + def postTransform(tree: Tree): Tree = atPhase(phase.next) { + def applyUnary(tree: Tree): Tree = + if (tree.symbol.isMethod && (!tree.tpe.isInstanceOf[PolyType] || tree.tpe.typeParams.isEmpty)) { + if (!tree.tpe.isInstanceOf[MethodType]) tree.tpe = MethodType(List(), tree.tpe); + atPos(tree.pos)(Apply(tree, List()) setType tree.tpe.resultType) + } else if (tree.isType && !tree.isInstanceOf[TypeTree]) { + TypeTree(tree.tpe) setPos tree.pos + } else { + tree + } + tree match { + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + copy.DefDef(tree, mods, name, tparams, List(List.flatten(vparamss)), tpt, rhs); + case Try(body, catches, finalizer) => + if (catches forall treeInfo.isCatchCase) tree + else { + val exname = unit.fresh.newName("ex$"); + val cases = + if (catches exists treeInfo.isDefaultCase) catches + else catches ::: List(CaseDef(Ident(nme.WILDCARD), EmptyTree, Throw(Ident(exname)))); + val catchall = + atPos(tree.pos) { + CaseDef( + Bind(exname, Ident(nme.WILDCARD)), + EmptyTree, + Match(Ident(exname), cases)) + } + if (settings.debug.value) log("rewrote try: " + catches + " ==> " + catchall); + val catches1 = typer.atOwner(currentOwner).typedCases( + tree, List(catchall), ThrowableClass.tpe, WildcardType); + copy.Try(tree, body, catches1, finalizer) + } + case Apply(Apply(fn, args), args1) => + copy.Apply(tree, fn, args ::: args1) + case Ident(name) => + if (name == nme.WILDCARD_STAR.toTypeName) + unit.error(tree.pos, " argument does not correspond to `*'-parameter"); + applyUnary(tree); + case Select(_, _) => + applyUnary(tree) + case TypeApply(_, _) => + applyUnary(tree) + case _ => + tree + } + } + } + + private val resetAttrs = new Traverser { + override def traverse(tree: Tree): unit = tree match { + case EmptyTree | TypeTree() => + ; + case _ => + if (tree.hasSymbol) tree.symbol = NoSymbol; + tree.tpe = null; + super.traverse(tree) + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala new file mode 100644 index 0000000000..e895524915 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -0,0 +1,43 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +/** The main attribution phase. + */ +[_trait_] abstract class Analyzer + extends AnyRef + with Contexts + with Namers + with Typers + with Infer + with Variances + with EtaExpansion + with SyntheticMethods + with Codification { + + val global: Global; + import global._; + + object namerFactory extends SubComponent { + val global: Analyzer.this.global.type = Analyzer.this.global; + val phaseName = "namer"; + def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) { + def apply(unit: CompilationUnit): unit = + new Namer(startContext.make(unit)).enterSym(unit.body); + } + } + + object typerFactory extends SubComponent { + val global: Analyzer.this.global.type = Analyzer.this.global; + val phaseName = "typer"; + def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) { + resetTyper; + def apply(unit: CompilationUnit): unit = + unit.body = newTyper(startContext.make(unit)).typed(unit.body) + } + } +} + diff --git a/src/compiler/scala/tools/nsc/typechecker/Codification.scala b/src/compiler/scala/tools/nsc/typechecker/Codification.scala new file mode 100644 index 0000000000..1deda7bdfb --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Codification.scala @@ -0,0 +1,207 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import symtab.Flags._; +import scala.collection.immutable.ListMap; +import scala.tools.nsc.util.{ListBuffer, FreshNameCreator}; + +[_trait_] abstract class Codification: Analyzer { + + import global._; + + case class FreeValue(tree: Tree) extends reflect.Code; + + type ReifyEnvironment = ListMap[Symbol, reflect.Symbol]; + + class Reifier(env: ReifyEnvironment, currentOwner: reflect.Symbol) { + + def reify(tree: Tree): reflect.Code = tree match { + case Ident(_) => + val rsym = reify(tree.symbol); + if (rsym == reflect.NoSymbol) FreeValue(tree) + else reflect.Ident(rsym) + case Select(qual, _) => + val rsym = reify(tree.symbol); + if (rsym == reflect.NoSymbol) throw new TypeError("cannot reify symbol: " + tree.symbol) + else reflect.Select(reify(qual), reify(tree.symbol)) + case Literal(constant) => + reflect.Literal(constant.value) + case Apply(fun, args) => + reflect.Apply(reify(fun), args map reify) + case TypeApply(fun, args) => + reflect.TypeApply(reify(fun), args map (.tpe) map reify) + case Function(vparams, body) => + var env1 = env; + for (val vparam <- vparams) { + val local = reflect.LocalValue( + currentOwner, vparam.symbol.name.toString(), reify(vparam.symbol.tpe)); + env1 = env1.update(vparam.symbol, local); + } + reflect.Function(vparams map (.symbol) map env1, + new Reifier(env1, currentOwner).reify(body)) + case This(_) => + reflect.This(reify(tree.symbol)) + case _ => + throw new TypeError("cannot reify tree: " + tree) + } + + private def mkGlobalSymbol(fullname: String, sym: Symbol): reflect.Symbol = + if (sym.isClass) reflect.Class(fullname) + else if (sym.isType) reflect.TypeField(fullname, reify(sym.info)) + else if (sym.isMethod) reflect.Method(fullname, reify(sym.info)) + else reflect.Field(fullname, reify(sym.info)); + + def reify(sym: Symbol): reflect.Symbol = env.get(sym) match { + case Some(rsym) => + rsym + case None => + if (sym.isRoot || sym.isRootPackage || sym.isEmptyPackageClass || sym.isEmptyPackage) + reflect.RootSymbol + else reify(sym.owner) match { + case reflect.NoSymbol => + reflect.NoSymbol; + case reflect.RootSymbol => + mkGlobalSymbol(sym.name.toString(), sym) + case reflect.Class(ownername) => + mkGlobalSymbol(ownername + "." + sym.name, sym) + case _ => + reflect.NoSymbol + } + } + + def reify(tp: Type): reflect.Type = null /*tp match { + case NoPrefix => + reflect.NoPrefix + case NoType => + reflect.NoType + case TypeRef(pre, sym, args) => + val tp = if (sym.owner.isPackageClass) reflect.NamedType(sym.fullNameString); + else reflect.PrefixedType(reify(pre), reify(sym)); + if (args.isEmpty) tp else reflect.AppliedType(tp, args map reify) + case SingleType(pre, sym) => + reflect.SingleType(reify(pre), reify(sym)) + case ThisType(clazz) => + reflect.ThisType(reify(clazz)) + case TypeBounds(lo, hi) => + reflect.TypeBounds(reify(lo), reify(hi)) + case MethodType(formals, restp) => + val formals1 = formals map reify; + val restp1 = reify(restp); + if (tp.isInstanceOf[ImplicitMethodType]) new reflect.ImplicitMethodType(formals1, restp1) + else reflect.MethodType(formals1, restp1) + //case PolyType(typeParams, ClassInfoType(parents, decls, symbol)) => reflect.PolyType(typeParams map reify, Nil, resultType) + //case PolyType(typeParams, SingleType(pre, sym)) => reflect.PolyType(typeParams map reify, Nil, resultType) + case PolyType(typeParams, MethodType(paramsList, resultType)) => + System.err.println("poly polyyyy"); + reflect.PolyType(Nil, Nil , reify(resultType)) //typeParams map mkTypeBounds + case _ => + throw new TypeError("cannot reify type: " + tp) + }*/ + } + + type InjectEnvironment = ListMap[reflect.Symbol, Name]; + + class Injector(env: InjectEnvironment, fresh: FreshNameCreator) { + + // todo replace className by caseName in CaseClass once we have switched to nsc. + def className(value: CaseClass): String = value match { + case _ :: _ => "scala.$colon$colon" + case reflect.Ident(_) => "scala.reflect.Ident" + case reflect.Select(_, _) => "scala.reflect.Select" + case reflect.Literal(_) => "scala.reflect.Literal" + case reflect.Apply(_, _) => "scala.reflect.Apply" + case reflect.TypeApply(_, _) => "scala.reflect.TypeApply" + case reflect.Function(_, _) => "scala.reflect.Function" + case reflect.Class(_) => "scala.reflect.Class" + case reflect.Method(_, _) => "scala.reflect.Method" + case reflect.Field(_, _) => "scala.reflect.Field" + case reflect.This(_) => "scala.reflect.This" + case reflect.NamedType(_) => "scala.reflect.NamedType" + case reflect.PrefixedType(_, _) => "scala.reflect.PrefixedType" + case reflect.SingleType(_, _) => "scala.reflect.SingleType" + case reflect.ThisType(_) => "scala.reflect.ThisType" + case reflect.AppliedType(_, _) => "scala.reflect.AppliedType" + case reflect.TypeBounds(_, _) => "scala.reflect.TypeBounds" + case reflect.MethodType(_, _) => + if (value.isInstanceOf[reflect.ImplicitMethodType]) "scala.reflect.ImplicitMethodType" else "scala.reflect.MethodType" + case reflect.PolyType(_, _, _) => "scala.reflect.PolyType" + case _ => + "" + } + + def objectName(value: Any): String = value match { + case Nil => "scala.Nil" + case reflect.NoSymbol => "scala.reflect.NoSymbol" + case reflect.RootSymbol => "scala.reflect.RootSymbol" + case reflect.NoPrefix => "scala.reflect.NoPrefix" + case reflect.NoType => "scala.reflect.NoType" + case _ => "" + } + + def injectType(name: String): Tree = TypeTree(definitions.getClass(name).initialize.tpe); + + def inject(value: Any): Tree = value match { + case FreeValue(tree) => + tree + case reflect.Function(params, body) => + var env1 = env; + val vdefs = for (val param <- params) yield { + val lname = newTermName(fresh.newName()); + env1 = env1.update(param, lname); + ValDef(NoMods, lname, injectType("scala.reflect.LocalValue"), + New(injectType("scala.reflect.LocalValue"), + List(List(inject(param.owner), inject(param.name), inject(param.tpe))))) + } + Block(vdefs, new Injector(env1, fresh).inject(body)) + case rsym: reflect.LocalSymbol => + Ident(env(rsym)) + case x: String => Literal(Constant(x)) + case x: Boolean => Literal(Constant(x)) + case x: Byte => Literal(Constant(x)) + case x: Short => Literal(Constant(x)) + case x: Char => Literal(Constant(x)) + case x: Int => Literal(Constant(x)) + case x: Long => Literal(Constant(x)) + case x: Float => Literal(Constant(x)) + case x: Double => Literal(Constant(x)) + case c: CaseClass => + val name = objectName(c); + if (name.length() != 0) gen.mkRef(definitions.getModule(name)) + else { + val name = className(c); + if (name.length() == 0) throw new Error("don't know how to inject " + value); + val injectedArgs = new ListBuffer[Tree]; + for (val i <- Iterator.range(0, c.caseArity)) + injectedArgs += inject(c.caseElement(i)); + New(Ident(definitions.getClass(name)), List(injectedArgs.toList)) + } + case null => gen.mkRef(definitions.getModule("scala.reflect.NoType")) + case _ => throw new Error("don't know how to inject " + value); + } + } + + def reify(tree: Tree): reflect.Code = + new Reifier(ListMap.Empty, reflect.NoSymbol).reify(tree); + + def inject(code: reflect.Code): Tree = + new Injector(ListMap.Empty, new FreshNameCreator).inject(code); + + /** returns + * < new TypedCode[T](tree1) > + * where T = tree.tpe + * tree1 = inject(reify(tree)) + */ + def codify(tree: Tree): Tree = { + val reified = reify(tree); + if (settings.debug.value) log("reified = " + reified); + val injected = inject(reified); + if (settings.debug.value) log("injected = " + injected); + New(TypeTree(appliedType(definitions.TypedCodeClass.typeConstructor, List(tree.tpe))), + List(List(injected))); + + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala new file mode 100644 index 0000000000..0afb18d8d5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala @@ -0,0 +1,145 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +abstract class ConstantFolder { + + val global: Global; + import global._; + import definitions._; + + /** If tree is a constant operation, replace with result. */ + def apply(tree: Tree): Tree = fold(tree, tree match { + case Apply(Select(Literal(x), op), List(Literal(y))) => foldBinop(op, x, y) + case Select(Literal(x), op) => foldUnop(op, x) + case _ => null + }); + + /** If tree is a constant value that can be converted to type `pt', perform the conversion */ + def apply(tree: Tree, pt: Type): Tree = fold(tree, tree.tpe match { + case ConstantType(x) => x convertTo pt + case _ => null + }); + + private def fold(tree: Tree, x: Constant): Tree = + if (x != null && x.tag != UnitTag) tree setType ConstantType(x) + else tree; + + private def foldUnop(op: Name, x: Constant): Constant = Pair(op, x.tag) match { + case Pair(nme.ZNOT, BooleanTag) => Constant(!x.booleanValue) + + case Pair(nme.NOT , IntTag ) => Constant(~x.intValue) + case Pair(nme.NOT , LongTag ) => Constant(~x.longValue) + + case Pair(nme.ADD , IntTag ) => Constant(+x.intValue) + case Pair(nme.ADD , LongTag ) => Constant(+x.longValue) + case Pair(nme.ADD , FloatTag ) => Constant(+x.floatValue) + case Pair(nme.ADD , DoubleTag ) => Constant(+x.doubleValue) + + case Pair(nme.SUB , IntTag ) => Constant(-x.intValue) + case Pair(nme.SUB , LongTag ) => Constant(-x.longValue) + case Pair(nme.SUB , FloatTag ) => Constant(-x.floatValue) + case Pair(nme.SUB , DoubleTag ) => Constant(-x.doubleValue) + + case _ => null + } + + private def foldBinop(op: Name, x: Constant, y: Constant): Constant = { + val optag = if (x.tag > y.tag) x.tag else y.tag; + optag match { + case BooleanTag => + 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 + } + case ByteTag | ShortTag | CharTag | IntTag => + 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 + } + case LongTag => + 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 + } + case FloatTag => + 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 + } + case DoubleTag => + 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 + } + case StringTag => + op match { + case nme.ADD => Constant(x.stringValue + y.stringValue) + case _ => null + } + case _ => + null + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala new file mode 100644 index 0000000000..021e810a43 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -0,0 +1,330 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import symtab.Flags._; +import scala.tools.nsc.util.Position; + +[_trait_] abstract class Contexts: Analyzer { + import global._; + + val NoContext = new Context { + override def implicitss: List[List[ImplicitInfo]] = List(); + } + NoContext.enclClass = NoContext; + + val startContext = { + import definitions._; + var sc = NoContext.make( + Template(List(), List()) setSymbol NoSymbol setType NoType, + definitions.RootClass, + definitions.RootClass.info.decls); + def addImport(pkg: Symbol): unit = { + val qual = gen.mkStableRef(pkg); + sc = sc.makeNewImport( + Import(qual, List(Pair(nme.WILDCARD, null))) + setSymbol NoSymbol.newImport(Position.NOPOS).setInfo(ImportType(qual)) + setType NoType); + sc.depth = sc.depth + 1 + } + if (!settings.noimports.value) { + addImport(JavaLangPackage); + addImport(ScalaPackage); + if (!settings.nopredefs.value) + addImport(PredefModule); + } + sc + } + + def resetContexts: unit = { + var sc = startContext; + while (sc != NoContext) { + sc.tree match { + case Import(qual, _) => qual.tpe = singleType(qual.symbol.owner.thisType, qual.symbol); + case _ => + } + sc = sc.outer + } + } + + class Context { + var unit: CompilationUnit = _; + var tree: Tree = _; // Tree associated with this context + var owner: Symbol = NoSymbol; // The current owner + var scope: Scope = _; // The current scope + var outer: Context = _; // The next outer context + var enclClass: Context = _; // The next outer context whose tree is a + // template or package definition + var variance: int = _; // Variance relative to enclosing class. + private var _undetparams: List[Symbol] = List(); // Undetermined type parameters + var depth: int = 0; + var imports: List[ImportInfo] = List(); + + var prefix: Type = NoPrefix; + var inConstructorSuffix = false; // are we in a secondary constructor + // after the this constructor call? + var reportAmbiguousErrors = false; + var reportGeneralErrors = false; + var checking = false; + + var savedTypeBounds: List[Pair[Symbol, Type]] = List(); + + def undetparams = _undetparams; + def undetparams_=(ps: List[Symbol]) = { + //System.out.println("undetparams = " + ps);//debug + _undetparams = ps + } + + def make(unit: CompilationUnit, tree: Tree, owner: Symbol, scope: Scope, imports: List[ImportInfo]): Context = { + val c = new Context; + c.unit = unit; + c.tree = tree; + c.owner = owner; + c.scope = scope; + tree match { + case Template(_, _) | PackageDef(_, _) => + c.enclClass = c; + c.prefix = skolemizedThisType(this.tree, this.prefix, c.owner); + c.inConstructorSuffix = false; + case _ => + c.enclClass = this.enclClass; + c.prefix = if (c.owner != this.owner && c.owner.isTerm) NoPrefix else this.prefix; + c.inConstructorSuffix = this.inConstructorSuffix; + } + c.variance = this.variance; + c.depth = if (scope == this.scope) this.depth else this.depth + 1; + c.imports = imports; + c.reportAmbiguousErrors = this.reportAmbiguousErrors; + c.reportGeneralErrors = this.reportGeneralErrors; + c.checking = this.checking; + c.outer = this; + c + } + + def make(unit: CompilationUnit): Context = { + val c = make(unit, EmptyTree, owner, scope, imports); + c.reportAmbiguousErrors = true; + c.reportGeneralErrors = true; + c + } + + def makeNewImport(imp: Import): Context = + make(unit, imp, owner, scope, new ImportInfo(imp, depth) :: imports); + + def make(tree: Tree, owner: Symbol, scope: Scope): Context = + make(unit, tree, owner, scope, imports); + + def makeNewScope(tree: Tree, owner: Symbol): Context = + make(tree, owner, new Scope(scope)); + + def make(tree: Tree, owner: Symbol): Context = + make(tree, owner, scope); + + def make(tree: Tree): Context = + make(tree, owner); + + def makeImplicit(reportAmbiguousErrors: boolean) = { + val c = make(tree); + c.reportAmbiguousErrors = reportAmbiguousErrors; + c.reportGeneralErrors = false; + c + } + + def makeConstructorContext = { + val baseContext = enclClass.outer; + val argContext = baseContext.makeNewScope(tree, owner); + for (val sym <- scope.toList) argContext.scope enter sym; + argContext + } + + def makeConstructorSuffixContext = { + val c = make(tree); + c.inConstructorSuffix = true; + c + } + + def skolemizedThisType(encl: Tree, pre: Type, clazz: Symbol): Type = if (settings.Xgadt.value) { + encl match { + case ClassDef(_, _, tparamdefs, _, _) => + System.out.println("sktt " + clazz); + if (!tparamdefs.isEmpty || pre.isInstanceOf[SingleType]) { + val tparams = clazz.unsafeTypeParams; + val tskolems = tparamdefs map (.symbol); + System.out.println("sktt2 " + tparams + " " + tskolems); + val self = clazz.newThisSkolem setInfo clazz.typeOfThis.substSym(tparams, tskolems); + singleType(pre, self) + } else clazz.thisType + case _ => + clazz.thisType + } + } else clazz.thisType; + + def error(pos: int, msg: String): unit = + if (reportGeneralErrors) + unit.error(pos, if (checking) "**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg) + else + throw new TypeError(msg); + + def ambiguousError(pos: int, pre: Type, sym1: Symbol, sym2: Symbol, rest: String): unit = { + val msg = + ("ambiguous reference to overloaded definition,\n" + + "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + + "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + + "\nmatch " + rest); + if (reportAmbiguousErrors) unit.error(pos, msg) + else throw new TypeError(msg); + } + + def outerContext(clazz: Symbol): Context = { + var c = this; + while (c != NoContext && c.owner != clazz) c = c.outer.enclClass; + c + } + + def isLocal(): boolean = tree match { + case Block(_,_) => true + case PackageDef(_, _) => false + case EmptyTree => false + case _ => outer.isLocal() + } + + def nextEnclosing(p: Context => boolean): Context = + if (this == NoContext || p(this)) this else outer.nextEnclosing(p); + + override def toString(): String = { + if (this == NoContext) "NoContext"; + else owner.toString() + " @ " + tree.getClass() + " " + tree.toString() + ", scope = " + scope.hashCode() + " " + scope.toList + "\n:: " + outer.toString() + } + + /** Is `sym' accessible as a member of tree `site' with type `pre' in current context? + */ + def isAccessible(sym: Symbol, pre: Type, superAccess: boolean): boolean = { + + /** Are we inside definition of `owner'? */ + def accessWithin(owner: Symbol): boolean = { + var c = this; + while (c != NoContext && c.owner != owner) { + if (c.outer == null) assert(false, "accessWithin(" + owner + ") " + c);//debug + if (c.outer.enclClass == null) assert(false, "accessWithin(" + owner + ") " + c);//debug + c = c.outer.enclClass; + } + c != NoContext + } + + /** Is `clazz' a subclass of an enclosing class? */ + def isSubClassOfEnclosing(clazz: Symbol): boolean = { + var c = this; + while (c != NoContext && !clazz.isSubClass(c.owner)) c = c.outer.enclClass; + c != NoContext; + } + + (pre == NoPrefix + || + (!sym.hasFlag(PRIVATE | PROTECTED)) + || + accessWithin(sym.owner) && (!sym.hasFlag(LOCAL) || pre =:= sym.owner.thisType) + || + (!sym.hasFlag(PRIVATE) && + (superAccess || + (pre.widen.symbol.isSubClass(sym.owner) && isSubClassOfEnclosing(pre.widen.symbol))))) + } + + def pushTypeBounds(sym: Symbol): unit = { + savedTypeBounds = Pair(sym, sym.info) :: savedTypeBounds + } + + def restoreTypeBounds: unit = { + for (val Pair(sym, info) <- savedTypeBounds) { + System.out.println("resetting " + sym + " to " + info); + sym.setInfo(info); + } + savedTypeBounds = List() + } + + private var implicitsCache: List[List[ImplicitInfo]] = null; + private var implicitsRun: CompilerRun = NoRun; + + private def collectImplicits(syms: List[Symbol], pre: Type): List[ImplicitInfo] = + for (val sym <- syms; sym.hasFlag(IMPLICIT) && isAccessible(sym, pre, false)) + yield ImplicitInfo(sym.name, pre.memberType(sym), sym); + + private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = { + val pre = imp.qual.tpe; + def collect(sels: List[Pair[Name, Name]]): List[ImplicitInfo] = sels match { + case List() => List() + case List(Pair(nme.WILDCARD, _)) => collectImplicits(pre.implicitMembers, pre) + case Pair(from, to) :: sels1 => + var impls = collect(sels1) filter (info => info.name != from); + if (to != nme.WILDCARD) { + val sym = imp.importedSymbol(to); + if (sym.hasFlag(IMPLICIT)) impls = ImplicitInfo(to, pre.memberType(sym), sym) :: impls; + } + impls + } + if (settings.debug.value) log("collect implicit imports " + imp + "=" + collect(imp.tree.selectors));//debug + collect(imp.tree.selectors) + } + + def implicitss: List[List[ImplicitInfo]] = { + if (implicitsRun != currentRun) { + implicitsRun = currentRun; + val newImplicits: List[ImplicitInfo] = + if (owner != outer.owner && owner.isClass && !owner.isPackageClass) { + if (!owner.isInitialized) return outer.implicitss; + if (settings.debug.value) log("collect member implicits " + owner + ", implicit members = " + owner.thisType.implicitMembers);//debug + collectImplicits(owner.thisType.implicitMembers, owner.thisType) + } else if (scope != outer.scope && !owner.isPackageClass) { + if (settings.debug.value) log("collect local implicits " + scope.toList);//debug + collectImplicits(scope.toList, NoPrefix) + } else if (imports != outer.imports) { + assert(imports.tail == outer.imports); + collectImplicitImports(imports.head) + } else List(); + implicitsCache = if (newImplicits.isEmpty) outer.implicitss + else newImplicits :: outer.implicitss; + } + implicitsCache + } + } + + class ImportInfo(val tree: Import, val depth: int) { + + /** The prefix expression */ + def qual: Tree = tree.symbol.info match { + case ImportType(expr) => expr + case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info);//debug + } + + /** Is name imported explicitly, not via wildcard? */ + def isExplicitImport(name: Name): boolean = + tree.selectors exists (._2.==(name.toTermName)); + + /** The symbol with name `name' imported from import clause `tree'. + */ + def importedSymbol(name: Name): Symbol = { + var result: Symbol = NoSymbol; + var renamed = false; + var selectors = tree.selectors; + while (selectors != Nil && result == NoSymbol) { + if (selectors.head._2 == name.toTermName) + result = qual.tpe.member( + if (name.isTypeName) selectors.head._1.toTypeName else selectors.head._1); + else if (selectors.head._1 == name.toTermName) + renamed = true + else if (selectors.head._1 == nme.WILDCARD && !renamed) + result = qual.tpe.member(name); + selectors = selectors.tail + } + result + } + + override def toString() = tree.toString(); + } + + case class ImplicitInfo(val name: Name, val tpe: Type, val sym: Symbol); + + case class ImportType(expr: Tree) extends Type; +} diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala new file mode 100644 index 0000000000..f6d481d58b --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -0,0 +1,73 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import util.ListBuffer; +import symtab.Flags._; + +[_trait_] abstract class EtaExpansion: Analyzer { + + import global._; + import posAssigner.atPos; + + /** Expand partial function applications of type `type'. + * + * p.f(es_1)...(es_n) + * ==> { + * private synthetic val eta$f = p.f // if p is not stable + * ... + * private synthetic val 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)) + * } + * tree is already attributed + */ + def etaExpand(tree: Tree): Tree = { + val tpe = tree.tpe; + var cnt = 0; + def freshName() = { cnt = cnt + 1; newTermName("eta$" + cnt) } + val defs = new ListBuffer[Tree]; + + /** Append to `defs' value definitions for all non-stable subexpressions + * of the function application `tree' */ + def liftoutPrefix(tree: Tree): Tree = { + def liftout(tree: Tree): Tree = + if (treeInfo.isPureExpr(tree)) tree + else { + val vname: Name = freshName(); + defs += atPos(tree.pos)(ValDef(Modifiers(SYNTHETIC), vname, TypeTree(), tree)); + Ident(vname) setPos tree.pos + } + tree match { + case Apply(fn, args) => + copy.Apply(tree, liftoutPrefix(fn), List.mapConserve(args)(liftout)) setType null + case TypeApply(fn, args) => + copy.TypeApply(tree, liftoutPrefix(fn), args) setType null + case Select(qual, name) => + copy.Select(tree, liftout(qual), name) setSymbol NoSymbol setType null + case Ident(name) => + tree + } + } + + /** Eta-expand lifted tree */ + def expand(tree: Tree, tpe: Type): Tree = tpe match { + case mt: ImplicitMethodType => + tree + case MethodType(formals, restpe) => + val params = formals map (formal => + ValDef(Modifiers(SYNTHETIC | PARAM), freshName(), TypeTree().setType(formal), EmptyTree)); + val args = params map (param => Ident(param.name)); + atPos(tree.pos)(Function(params, expand(Apply(tree, args), restpe))) + case _ => + tree + } + + val tree1 = liftoutPrefix(tree); + atPos(tree.pos)(Block(defs.toList, expand(tree1, tpe))) + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala new file mode 100644 index 0000000000..fb03509627 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -0,0 +1,657 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +[_trait_] abstract class Infer: Analyzer { + import symtab.Flags._; + import global._; + import definitions._; + import posAssigner.atPos; + import util.ListBuffer; + + var normM = 0; + var normP = 0; + var normO = 0; + +/* -- Type parameter inference utility functions -------------------------------------- */ + + /** The formal parameter types corresponding to `formals'. + * If `formals' has a repeated last parameter, a list of + * (nargs - params.length + 1) copies of its type is returned. */ + def formalTypes(formals: List[Type], nargs: int): List[Type] = { + val formals1 = formals map { + case TypeRef(_, sym, List(arg)) if (sym == ByNameParamClass) => arg + case formal => formal + } + if (!formals1.isEmpty && (formals1.last.symbol == RepeatedParamClass)) { + val ft = formals1.last.typeArgs.head; + formals1.init ::: (for (val i <- List.range(formals1.length - 1, nargs)) yield ft) + } else formals1 + } + + /** A fresh type varable with given type parameter as origin. */ + def freshVar(tparam: Symbol): TypeVar = new TypeVar(tparam.tpe, new TypeConstraint); + + //todo: remove comments around following privates; right now they cause an IllegalAccess + // error when built with scalac + + /*private*/ class NoInstance(msg: String) extends RuntimeException(msg); + + /*private*/ class DeferredNoInstance(getmsg: () => String) extends NoInstance("") { + override def getMessage(): String = getmsg() + } + + /*private*/ object instantiateMap extends TypeMap { + def apply(t: Type): Type = instantiate(t) + } + + /** map every TypeVar to its constraint.inst field. + * throw a NoInstance exception if a NoType or WildcardType is encountered. + * @throws NoInstance + */ + def instantiate(tp: Type): Type = tp match { + case WildcardType | NoType => + throw new NoInstance("undetermined type"); + case TypeVar(origin, constr) => + assert(constr.inst != null);//debug + if (constr.inst != NoType) instantiate(constr.inst) + else throw new DeferredNoInstance(() => + "no unique instantiation of type variable " + origin + " could be found"); + case _ => + instantiateMap.mapOver(tp) + } + + /** Is type fully defined, i.e. no embedded anytypes or wildcards in it? */ + def isFullyDefined(tp: Type): boolean = try { + instantiate(tp); true + } catch { + case ex: NoInstance => false + } + + /** Do type arguments `targs' conform to formal parameters `tparams'? */ + private def isWithinBounds(tparams: List[Symbol], targs: List[Type]): boolean = { + val bounds = tparams map (.info.subst(tparams, targs).bounds); + List.map2(bounds, targs)((bound, targ) => bound containsType targ) forall (x => x) + } + + /** Solve constraint collected in types `tvars' + * @param tvars All type variables to be instantiated. + * @param tparams The type parameters corresponding to `tvars' + * @param variances The variances of type parameters; need to reverse + * solution direction for all contravariant variables. + * @param upper When `true' search for max solution else min. + * @throws NoInstance + */ + private def solve(tvars: List[TypeVar], tparams: List[Symbol], variances: List[int], + upper: boolean): List[Type] = { + val config = tvars zip (tparams zip variances); + + def solveOne(tvar: TypeVar, tparam: Symbol, variance: int): unit = { + if (tvar.constr.inst == NoType) { + val up = if (variance != CONTRAVARIANT) upper else !upper; + tvar.constr.inst = null; + val bound: Type = if (up) tparam.info.bounds.hi else tparam.info.bounds.lo; + var cyclic = false; + for (val Pair(tvar2, Pair(tparam2, variance2)) <- config) { + if (tparam2 != tparam && + ((bound contains tparam2) || + up && (tparam2.info.bounds.lo =:= tparam.tpe) || + !up && (tparam2.info.bounds.hi =:= tparam.tpe))) { + if (tvar2.constr.inst == null) cyclic = true; + solveOne(tvar2, tparam2, variance2); + } + } + if (!cyclic) { + if (up) { + if (bound.symbol != AnyClass) { + tvar.constr.hibounds = + bound.subst(tparams, tvars) :: tvar.constr.hibounds; + } + for (val tparam2 <- tparams) + if (tparam2.info.bounds.lo =:= tparam.tpe) + tvar.constr.hibounds = + tparam2.tpe.subst(tparams, tvars) :: tvar.constr.hibounds; + } else { + if (bound.symbol != AllClass && bound.symbol != tparam) { + tvar.constr.lobounds = + bound.subst(tparams, tvars) :: tvar.constr.lobounds; + } + for (val tparam2 <- tparams) + if (tparam2.info.bounds.hi =:= tparam.tpe) + tvar.constr.lobounds = + tparam2.tpe.subst(tparams, tvars) :: tvar.constr.lobounds; + } + } + tvar.constr.inst = if (up) glb(tvar.constr.hibounds) else lub(tvar.constr.lobounds) + } + } + for (val Pair(tvar, Pair(tparam, variance)) <- config) solveOne(tvar, tparam, variance); + tvars map instantiate; + } + + def skipImplicit(tp: Type) = if (tp.isInstanceOf[ImplicitMethodType]) tp.resultType else tp; + + /** Automatically perform the following conversions on expression types: + * A method type becomes the corresponding function type. + * A nullary method type becomes its result type. + * Implicit parameters are skipped. + */ + def normalize(tp: Type): Type = skipImplicit(tp) match { + case MethodType(formals, restpe) => + if (util.Statistics.enabled) normM = normM + 1; + functionType(formals, normalize(restpe)) + case PolyType(List(), restpe) => + if (util.Statistics.enabled) normP = normP + 1; + normalize(restpe); + case tp1 => + if (util.Statistics.enabled) normO = normO + 1; + tp1 + } + + /** The context-dependent inferencer part */ + class Inferencer(context: Context) { + + /* -- Error Messages ----------------------------------------------------- */ + + def setError[T <: Tree](tree: T): T = { + val name = newTermName("<error: " + tree + ">"); + if (tree.hasSymbol) + tree.setSymbol( + if (tree.isType) context.owner.newErrorClass(name.toTypeName) + else context.owner.newErrorValue(name)); + tree.setType(ErrorType) + } + + def decode(name: Name): String = + (if (name.isTypeName) "type " else "value ") + name.decode; + + def treeSymTypeMsg(tree: Tree): String = + if (tree.symbol == null) + "expression of type " + tree.tpe + else if (tree.symbol.hasFlag(OVERLOADED)) + "overloaded method " + tree.symbol + " with alternatives " + tree.tpe + else ( + tree.symbol.toString() + + (if (tree.tpe.paramSectionCount > 0) ": " else " of type ") + + tree.tpe + ); + + def applyErrorMsg(tree: Tree, msg: String, argtpes: List[Type], pt: Type) = ( + treeSymTypeMsg(tree) + msg + argtpes.mkString("(", ",", ")") + + (if (pt == WildcardType) "" else " with expected result type " + pt) + ); + + def foundReqMsg(found: Type, req: Type): String = + ";\n found : " + found.toLongString + "\n required: " + req; + + def error(pos: int, msg: String): unit = + context.error(pos, msg); + + def errorTree(tree: Tree, msg: String): Tree = { + error(tree.pos, msg); + setError(tree) + } + + def typeError(pos: int, found: Type, req: Type): unit = + if (!found.isError && !req.isError) { + error(pos, + "type mismatch" + foundReqMsg(found, req) + + (if (!(found.resultType eq found) && isCompatible(found.resultType, req)) + "\n possible cause: missing arguments for method or constructor" + else "")); + if (settings.explaintypes.value) + explainTypes(found, req); + } + + def typeErrorTree(tree: Tree, found: Type, req: Type): Tree = { + typeError(tree.pos, found, req); + setError(tree) + } + + /* -- Tests & Checks-------------------------------------------------------- */ + + /** Check that `sym' is defined and accessible as a member of tree `site' with type `pre' + * in current context. */ + def checkAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): Tree = + if (sym.isError) { + tree setSymbol sym setType ErrorType + } else { + sym.toplevelClass match { + case clazz : ClassSymbol => + // System.err.println("TOP: " + clazz + " " + clazz.sourceFile); + if (clazz.sourceFile != null) + global.currentRun.currentUnit.depends += clazz.sourceFile; + + case _ => + } + val sym1 = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])); + if (sym1 == NoSymbol) { + if (settings.debug.value) { + System.out.println(context);//debug + System.out.println(tree);//debug + System.out.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType));//debug + } + errorTree(tree, sym.toString() + " cannot be accessed in " + + (if (sym.isClassConstructor) context.enclClass.owner else pre.widen)) + } else { + var owntype = pre.memberType(sym1); + if (pre.isInstanceOf[SuperType]) + owntype = owntype.substSuper(pre, site.symbol.thisType); + tree setSymbol sym1 setType owntype + } + } + + def isCompatible(tp: Type, pt: Type): boolean = { + val tp1 = normalize(tp); + (tp1 <:< pt) || isCoercible(tp, pt) + } + + def isCoercible(tp: Type, pt: Type): boolean = false; + + def isCompatible(tps: List[Type], pts: List[Type]): boolean = + List.map2(tps, pts)((tp, pt) => isCompatible(tp, pt)) forall (x => x); + + /* -- Type instantiation------------------------------------------------------------ */ + + /** Return inferred type arguments of polymorphic expression, given + * its type parameters and result type and a prototype `pt'. + * If no minimal type variables exist that make the + * instantiated type a subtype of `pt', return null. + */ + private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type): List[Type] = { + val tvars = tparams map freshVar; + if (isCompatible(restpe.subst(tparams, tvars), pt)) { + try { + solve(tvars, tparams, tparams map varianceInType(restpe), false); + } catch { + case ex: NoInstance => null + } + } else null + } + + /** Return inferred proto-type arguments of function, given + * its type and value parameters and result type, and a + * prototype `pt' for the function result. + * Type arguments need to be either determined precisely by + * the prototype, or they are maximized, if they occur only covariantly + * in the value parameter list. + * If instantiation of a type parameter fails, + * take WildcardType for the proto-type argument. */ + def protoTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type, + pt: Type): List[Type] = { + /** Map type variable to its instance, or, if `variance' is covariant/contravariant, + * to its upper/lower bound; */ + def instantiateToBound(tvar: TypeVar, variance: int): Type = try { + if (tvar.constr.inst != NoType) { + instantiate(tvar.constr.inst) + } else if ((variance & COVARIANT) != 0 && !tvar.constr.hibounds.isEmpty) { + tvar.constr.inst = glb(tvar.constr.hibounds); + instantiate(tvar.constr.inst) + } else if ((variance & CONTRAVARIANT) != 0 && !tvar.constr.lobounds.isEmpty) { + tvar.constr.inst = lub(tvar.constr.lobounds); + instantiate(tvar.constr.inst) + } else { + WildcardType + } + } catch { + case ex: NoInstance => WildcardType + } + val tvars = tparams map freshVar; + if (isCompatible(restpe.subst(tparams, tvars), pt)) + List.map2(tparams, tvars) ((tparam, tvar) => + instantiateToBound(tvar, varianceInTypes(formals)(tparam))) + else + tvars map (tvar => WildcardType) + } + + /** Return inferred type arguments, given type parameters, formal parameters, + * argument types, result type and expected result type. + * If this is not possible, throw a `NoInstance' exception. + * Undetermined type arguments are represented by `definitions.AllClass.tpe'. + * No check that inferred parameters conform to their bounds is made here. + * @param tparams the type parameters of the method + * @param formals the value parameter types of the method + * @param restp the result type of the method + * @param argtpes the argument types of the application + * @param pt the expected return type of the application + * @param uninstantiated a listbuffer receiving all uninstantiated type parameters + * (type parameters mapped by the constraint solver to `scala.All' + * and not covariant in `restpe' are taken to be uninstantiated. + * Maps all those type arguments to their corresponding type + * parameters). + */ + private def methTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type, + argtpes: List[Type], pt: Type, + uninstantiated: ListBuffer[Symbol]): List[Type] = { + val tvars = tparams map freshVar; + if (formals.length != argtpes.length) { + throw new NoInstance("parameter lists differ in length"); + } + // check first whether type variables can be fully defined from + // expected result type. + if (!isCompatible(restpe.subst(tparams, tvars), pt)) { + throw new DeferredNoInstance(() => + "result type " + normalize(restpe) + " is incompatible with expected type " + pt) + } + for (val tvar <- tvars) + if (!isFullyDefined(tvar)) tvar.constr.inst = NoType; + + // Then define remaining type variables from argument types. + List.map2(argtpes, formals) {(argtpe, formal) => + if (!isCompatible(argtpe.deconst.subst(tparams, tvars), + formal.subst(tparams, tvars))) { + if (settings.explaintypes.value) + explainTypes(argtpe.deconst.subst(tparams, tvars), formal.subst(tparams, tvars)); + throw new DeferredNoInstance(() => + "argument expression's type is not compatible with formal parameter type" + + foundReqMsg(argtpe.deconst.subst(tparams, tvars), formal.subst(tparams, tvars))) + } + () + } + val targs = solve(tvars, tparams, tparams map varianceInTypes(formals), false); + List.map2(tparams, targs) {(tparam, targ) => + if (targ.symbol == AllClass && (varianceInType(restpe)(tparam) & COVARIANT) == 0) { + uninstantiated += tparam; + tparam.tpe + } else targ} + } + + + /** Is there an instantiation of free type variables `undetparams' such that + * function type `ftpe' is applicable to `argtpes' and its result conform to `pt'? */ + def isApplicable(undetparams: List[Symbol], ftpe: Type, argtpes: List[Type], pt: Type): boolean = + ftpe match { + case MethodType(formals0, restpe) => + val formals = formalTypes(formals0, argtpes.length); + if (undetparams.isEmpty) { + (formals.length == argtpes.length && + isCompatible(argtpes, formals) && + isCompatible(restpe, pt)) + } else { + try { + val uninstantiated = new ListBuffer[Symbol]; + val targs = methTypeArgs(undetparams, formals, restpe, argtpes, pt, uninstantiated); + val result = ( + exprTypeArgs(uninstantiated.toList, restpe.subst(undetparams, targs), pt) != null && + isWithinBounds(undetparams, targs) + ); + result + } catch { + case ex: NoInstance => false + } + } + case PolyType(tparams, restpe) => + val tparams1 = cloneSymbols(tparams); + isApplicable(tparams1 ::: undetparams, restpe.substSym(tparams, tparams1), argtpes, pt) + case ErrorType => + true + case _ => + false + } + + /** Does type `ftpe1' specialize type `ftpe2' + * when both are alternatives in an overloaded function? */ + def specializes(ftpe1: Type, ftpe2: Type): boolean = ftpe1 match { + case MethodType(formals, _) => + isApplicable(List(), ftpe2, formals, WildcardType) + case PolyType(tparams, MethodType(formals, _)) => + isApplicable(List(), ftpe2, formals, WildcardType) + case ErrorType => + true + case _ => + false + } + + /** error if arguments not within bounds. */ + def checkBounds(pos: int, tparams: List[Symbol], targs: List[Type], prefix: String): unit = + if (!isWithinBounds(tparams, targs)) { + error(pos, + prefix + "type arguments " + targs.mkString("[", ",", "]") + + " do not conform to " + tparams.head.owner + "'s type parameter bounds " + + (tparams map (.defString)).mkString("[", ",", "]")); + if (settings.explaintypes.value) { + val bounds = tparams map (.info.subst(tparams, targs).bounds); + List.map2(targs, bounds)((targ, bound) => explainTypes(bound.lo, targ)); + List.map2(targs, bounds)((targ, bound) => explainTypes(targ, bound.hi)); + () + } + } + + /** Substitite free type variables `undetparams' of polymorphic argument expression `tree', + * given two prototypes `strictPt', and `lenientPt'. + * `strictPt' is the first attempt prototype where type parameters + * are left unchanged. `lenientPt' is the fall-back prototype where type parameters + * are replaced by `WildcardType's. We try to instantiate first to `strictPt' and then, + * if this fails, to `lenientPt'. If both attempts fail, an error is produced. + */ + def inferArgumentInstance(tree: Tree, undetparams: List[Symbol], strictPt: Type, lenientPt: Type): unit = { + var targs = exprTypeArgs(undetparams, tree.tpe, strictPt); + if (targs == null) targs = exprTypeArgs(undetparams, tree.tpe, lenientPt); + substExpr(tree, undetparams, targs, lenientPt) + } + + /** Substitite free type variables `undetparams; of polymorphic expression `tree', + * given prototype `pt'. */ + def inferExprInstance(tree: Tree, undetparams: List[Symbol], pt: Type): unit = + substExpr(tree, undetparams, exprTypeArgs(undetparams, tree.tpe, pt), pt); + + /** Substitite free type variables `undetparams' of polymorphic argument expression `tree' to + * `targs', Error if `targs' is null */ + private def substExpr(tree: Tree, undetparams: List[Symbol], targs: List[Type], pt: Type): unit = + if (targs == null) { + error(tree.pos, "polymorphic expression cannot be instantiated to expected type" + + foundReqMsg(PolyType(undetparams, skipImplicit(tree.tpe)), pt)); + } else { + checkBounds(tree.pos, undetparams, targs, "inferred "); + new TreeTypeSubstituter(undetparams, targs).traverse(tree); + } + + /** Substitite free type variables `undetparams' of application `fn(args)', given prototype `pt'. + * Return the list of type parameters that remain uninstantiated. */ + def inferMethodInstance(fn: Tree, undetparams: List[Symbol], args: List[Tree], pt: Type): List[Symbol] = fn.tpe match { + case MethodType(formals, restpe) => + try { + val argtpes = args map (.tpe.deconst); + val uninstantiated = new ListBuffer[Symbol]; + val targs = methTypeArgs( + undetparams, formalTypes(formals, argtpes.length), restpe, argtpes, pt, uninstantiated); + checkBounds(fn.pos, undetparams, targs, "inferred "); + val treeSubst = new TreeTypeSubstituter(undetparams, targs); + treeSubst.traverse(fn); + treeSubst.traverseTrees(args); + uninstantiated.toList; + } catch { + case ex: NoInstance => + errorTree(fn, + "no type parameters for " + + applyErrorMsg( + fn, " exist so that it can be applied to arguments ", + args map (.tpe.widen), WildcardType) + + "\n --- because ---\n" + ex.getMessage()); + List() + } + } + + /** Substitite free type variables `undetparams' of type constructor `tree' in pattern, + * given prototype `pt'. + * return type substitution for type parameters. + */ + def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt: Type): unit = { + var restpe = skipImplicit(tree.tpe.resultType); + var tvars = undetparams map freshVar; + def computeArgs = + try { + val targs = solve(tvars, undetparams, undetparams map varianceInType(restpe), true); + checkBounds(tree.pos, undetparams, targs, "inferred "); + new TreeTypeSubstituter(undetparams, targs).traverse(tree) + } catch { + case ex: NoInstance => + errorTree(tree, "constructor of type " + restpe + + " can be instantiated in more than one way to expected type " + pt + + "\n --- because ---\n" + ex.getMessage()); + } + def instError = { + System.out.println("ici " + tree + " " + undetparams + " " + pt);//debug + if (settings.explaintypes.value) explainTypes(restpe.subst(undetparams, tvars), pt); + errorTree(tree, "constructor cannot be instantiated to expected type" + + foundReqMsg(restpe, pt)) + } + if (restpe.subst(undetparams, tvars) <:< pt) { + computeArgs + } else if (isFullyDefined(pt)) { + System.out.println("infer constr " + tree + ":" + restpe + ", pt = " + pt);//debug + val ptparams = freeTypeParams.collect(pt); + System.out.println("free type params = " + ptparams);//debug + val ptWithWildcards = pt.subst(ptparams, ptparams map (ptparam => WildcardType)); + tvars = undetparams map freshVar; + if (restpe.subst(undetparams, tvars) <:< ptWithWildcards) { + computeArgs; + restpe = skipImplicit(tree.tpe.resultType); + System.out.println("new tree = " + tree + ":" + restpe);//debug + val ptvars = ptparams map freshVar; + if (restpe <:< pt.subst(ptparams, ptvars)) { + for (val tvar <- ptvars) { + val tparam = tvar.origin.symbol; + val Pair(loBounds, hiBounds) = + if (tvar.constr.inst != NoType && isFullyDefined(tvar.constr.inst)) + Pair(List(tvar.constr.inst), List(tvar.constr.inst)) + else + Pair(tvar.constr.lobounds, tvar.constr.hibounds); + if (!loBounds.isEmpty || !hiBounds.isEmpty) { + context.nextEnclosing(.tree.isInstanceOf[CaseDef]).pushTypeBounds(tparam); + tparam setInfo TypeBounds( + lub(tparam.info.bounds.lo :: loBounds), + glb(tparam.info.bounds.hi :: hiBounds)); + System.out.println("new bounds of " + tparam + " = " + tparam.info);//debug + } + } + } else { System.out.println("no instance: "); instError } + } else { System.out.println("not a subtype " + restpe.subst(undetparams, tvars) + " of " + ptWithWildcards); instError } + } else { System.out.println("not fuly defined: " + pt); instError } + } + + /* -- Overload Resolution ----------------------------------------------------------- */ + + /** Assign `tree' the symbol and type of the alternative which matches + * prototype `pt', if it exists. + * If several alternatives match `pt', take parameterless one. + * If no alternative matches `pt', take the parameterless one anyway. + */ + def inferExprAlternative(tree: Tree, pt: Type): unit = tree.tpe match { + case OverloadedType(pre, alts) => tryTwice { + var alts1 = alts filter (alt => isCompatible(pre.memberType(alt), pt)); + if (alts1.isEmpty) alts1 = alts; + def improves(sym1: Symbol, sym2: Symbol): boolean = ( + sym2 == NoSymbol || + ((sym1.owner isSubClass sym2.owner) && + {val tp1 = pre.memberType(sym1); + val tp2 = pre.memberType(sym2); + (tp2 == ErrorType || + !global.typer.infer.isCompatible(tp2, pt) && global.typer.infer.isCompatible(tp1, pt) || + (tp2.paramSectionCount > 0) && (tp1.paramSectionCount == 0 || specializes(tp1, tp2)) + )}) + ); + val best = ((NoSymbol: Symbol) /: alts1) ((best, alt) => + if (improves(alt, best)) alt else best); + val competing = alts1 dropWhile (alt => best == alt || improves(best, alt)); + if (best == NoSymbol) { + tree match {//debug + case Select(qual, _) => + System.out.println("qual: " + qual + ":" + qual.tpe + " with decls " + qual.tpe.decls + " with members " + qual.tpe.members + " with members " + qual.tpe.member(newTermName("$minus"))); + case _ => + } + typeErrorTree(tree, tree.symbol.tpe, pt) + } else if (!competing.isEmpty) { + if (!pt.isError) + context.ambiguousError(tree.pos, pre, best, competing.head, "expected type " + pt); + setError(tree); + () + } else { + tree.setSymbol(best).setType(pre.memberType(best)) + } + } + } + + /** Assign `tree' the type of an alternative which is applicable to `argtpes', + * and whose result type is compatible with `pt'. + * If several applicable alternatives exist, take the + * most specialized one. + * If no applicable alternative exists, and pt != WildcardType, try again + * with pt = WildcardType. + * Otherwise, if there is no best alternative, error. + */ + def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], argtpes: List[Type], pt: Type): unit = tree.tpe match { + case OverloadedType(pre, alts) => tryTwice { + if (settings.debug.value) log("infer method alt " + tree.symbol + " with alternatives " + (alts map pre.memberType) + ", argtpes = " + argtpes + ", pt = " + pt);//debug + val alts1 = alts filter (alt => isApplicable(undetparams, pre.memberType(alt), argtpes, pt)); + def improves(sym1: Symbol, sym2: Symbol) = ( + sym2 == NoSymbol || sym2.isError || + ((sym1.owner isSubClass sym2.owner) && + specializes(pre.memberType(sym1), pre.memberType(sym2))) + ); + val best = ((NoSymbol: Symbol) /: alts1) ((best, alt) => + if (improves(alt, best)) alt else best); + val competing = alts1 dropWhile (alt => best == alt || improves(best, alt)); + if (best == NoSymbol) { + if (pt == WildcardType) { + errorTree(tree, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) + } else { + inferMethodAlternative(tree, undetparams, argtpes, WildcardType) + } + } else if (!competing.isEmpty) { + if (!(argtpes exists (.isError)) && !pt.isError) + context.ambiguousError(tree.pos, pre, best, competing.head, + "argument types " + argtpes.mkString("(", ",", ")") + + (if (pt == WildcardType) "" else " and expected result type " + pt)); + setError(tree); + () + } else { + tree.setSymbol(best).setType(pre.memberType(best)) + } + } + } + + /** Try inference twice, once without views and once with views, unless views are already disabled. + */ + def tryTwice(infer: => unit): unit = { + if (context.reportGeneralErrors) { + context.reportGeneralErrors = false; + try { + infer + } catch { + case ex: TypeError => + context.reportGeneralErrors = true; + infer + } + context.reportGeneralErrors = true + } else infer + } + + /** Assign `tree' the type of unique polymorphic alternative with `nparams' + * as the number of type parameters, if it exists. + * If several or none such polymorphic alternatives exist, error. + */ + def inferPolyAlternatives(tree: Tree, nparams: int): unit = tree.tpe match { + case OverloadedType(pre, alts) => + val sym = tree.symbol filter (alt => alt.typeParams.length == nparams); + if (sym == NoSymbol) { + errorTree(tree, + if (alts exists (alt => alt.typeParams.length > 0)) + "wrong number of type parameters for " + treeSymTypeMsg(tree) + else treeSymTypeMsg(tree) + " does not take type parameters") + } else if (sym.hasFlag(OVERLOADED)) { + val tparams = sym.alternatives.head.typeParams; + val tpe = + PolyType(tparams, + OverloadedType(AntiPolyType(pre, tparams map (.tpe)), sym.alternatives)); + sym.setInfo(tpe); + tree.setSymbol(sym).setType(tpe); + } else { + tree.setSymbol(sym).setType(pre.memberType(sym)) + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala new file mode 100644 index 0000000000..bb0aeed305 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -0,0 +1,576 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import scala.tools.nsc.util.Position; +import symtab.Flags; +import symtab.Flags._; + +/** Methods to create symbols and to enter them into scopes. */ +trait Namers: Analyzer { + import global._; + import definitions._; + + /** Convert to corresponding type parameters all skolems which satisfy one of the + * following two conditions: + * 1. The skolem is a parameter of a class or alias type + * 2. The skolem is a method parameter which appears in parameter `tparams' + */ + class DeSkolemizeMap(tparams: List[Symbol]) extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val tparam = sym.deSkolemize; + mapOver( + if (tparam == sym || !(tparams contains tparam)) tp + else rawTypeRef(NoPrefix, tparam, args)) + case SingleType(pre, sym) if (sym.isThisSkolem) => + ThisType(sym.deSkolemize) + case PolyType(tparams1, restpe) => + new DeSkolemizeMap(tparams1 ::: tparams).mapOver(tp) + case ClassInfoType(parents, decls, clazz) => + val parents1 = List.mapConserve(parents)(this); + if (parents1 eq parents) tp else ClassInfoType(parents1, decls, clazz); + case _ => + mapOver(tp) + } + } + + class Namer(val context: Context) { + + def setPrivate(sym: Symbol, mods: Modifiers): Symbol = sym; + + def updatePosFlags(sym: Symbol, pos: int, flags: int): Symbol = { + if (settings.debug.value) log("overwriting " + sym); + val lockedFlag = sym.flags & LOCKED; + sym.reset(NoType); + sym setPos pos; + sym.flags = flags | lockedFlag; + if (sym.isModule && sym.moduleClass != NoSymbol) + updatePosFlags(sym.moduleClass, pos, (flags & ModuleToClassFlags) | MODULE | FINAL); + if (sym.owner.isPackageClass && sym.linkedSym.rawInfo.isInstanceOf[loaders.SymbolLoader]) + // pre-set linked symbol to NoType, in case it is not loaded together with this symbol. + sym.linkedSym.setInfo(NoType); + sym + } + + private def isTemplateContext(context: Context): boolean = context.tree match { + case Template(_, _) => true + case Import(_, _) => isTemplateContext(context.outer) + case _ => false + } + + private var innerNamerCache: Namer = null; + def innerNamer: Namer = { + if (innerNamerCache == null) + innerNamerCache = if (!isTemplateContext(context)) this + else new Namer(context.make(context.tree, context.owner, new Scope())); + innerNamerCache + } + + private def doubleDefError(pos: int, sym: Symbol): unit = + context.error(pos, + sym.name.toString() + " is already defined as " + + (if (sym.hasFlag(CASE)) "case class " + sym.name else sym.toString())); + + def enterInScope(sym: Symbol): Symbol = { + if (!(sym.isSourceMethod && sym.owner.isClass)) { + val prev = context.scope.lookupEntry(sym.name); + if (prev != null && prev.owner == context.scope && !prev.sym.isSourceMethod) + doubleDefError(sym.pos, prev.sym); + } + context.scope enter sym; + sym + } + + private def enterPackageSymbol(pos: int, name: Name): Symbol = { + val cscope = if (context.owner == EmptyPackageClass) RootClass.info.decls + else context.scope; + val p: Symbol = cscope.lookup(name); + if (p.isPackage && cscope == p.owner.info.decls) { + p + } else { + val cowner = if (context.owner == EmptyPackageClass) RootClass else context.owner; + val pkg = cowner.newPackage(pos, name); + pkg.moduleClass.setInfo(new PackageClassInfoType(new Scope(), pkg.moduleClass)); + pkg.setInfo(pkg.moduleClass.tpe); + enterInScope(pkg) + } + } + + private def inConstructorFlag: long = + if (context.owner.isConstructor && !context.inConstructorSuffix) INCONSTRUCTOR + else 0l; + + private def enterClassSymbol(pos: int, flags: int, name: Name): Symbol = { + var c: Symbol = context.scope.lookup(name); + if (c.isType && c.isExternal && context.scope == c.owner.info.decls) { + updatePosFlags(c, pos, flags); + } else { + c = enterInScope(context.owner.newClass(pos, name)).setFlag(flags | inConstructorFlag); + } + if (c.owner.isPackageClass) currentRun.symSource(c) = context.unit.source.getFile(); + c + } + + private def enterModuleSymbol(pos: int, flags: int, name: Name): Symbol = { + var m: Symbol = context.scope.lookup(name); + if (m.isModule && !m.isPackage && m.isExternal && (context.scope == m.owner.info.decls)) { + updatePosFlags(m, pos, flags) + } else { + if (m.isTerm && !m.isPackage && m.isExternal && (context.scope == m.owner.info.decls)) + context.scope.unlink(m); + m = context.owner.newModule(pos, name); + m.setFlag(flags); + m.moduleClass.setFlag(flags | inConstructorFlag); + enterInScope(m) + } + if (m.owner.isPackageClass) currentRun.symSource(m) = context.unit.source.getFile(); + m + } + + private def enterCaseFactorySymbol(pos: int, flags: int, name: Name): Symbol = { + var m: Symbol = context.scope.lookup(name); + if (m.isTerm && !m.isPackage && m.isExternal && context.scope == m.owner.info.decls) { + updatePosFlags(m, pos, flags) + } else { + m = enterInScope(context.owner.newMethod(pos, name)).setFlag(flags); + } + if (m.owner.isPackageClass) currentRun.symSource(m) = context.unit.source.getFile(); + m + } + + def enterSyms(trees: List[Tree]): Namer = + (this /: trees) ((namer, tree) => namer.enterSym(tree)); + + def newTypeSkolems(tparams: List[Symbol]): List[Symbol] = { + val tskolems = tparams map (.newTypeSkolem); + val ltp = new LazyType { + override def complete(sym: Symbol): unit = + sym setInfo sym.deSkolemize.info.substSym(tparams, tskolems); + } + tskolems foreach (.setInfo(ltp)); + tskolems + } + + def skolemize(tparams: List[AbsTypeDef]): unit = if (settings.Xgadt.value) { + val tskolems = newTypeSkolems(tparams map (.symbol)); + for (val Pair(tparam, tskolem) <- tparams zip tskolems) tparam.symbol = tskolem + } + + def applicableTypeParams(owner: Symbol): List[Symbol] = + if (owner.isTerm || owner.isPackageClass) List() + else applicableTypeParams(owner.owner) ::: owner.unsafeTypeParams; + + def deSkolemize: TypeMap = new DeSkolemizeMap(applicableTypeParams(context.owner)); + + def enterSym(tree: Tree): Namer = { + + def finishWith(tparams: List[AbsTypeDef]): unit = { + if (settings.debug.value) log("entered " + tree.symbol + " in " + context.owner + ", scope-id = " + context.scope.hashCode()); + var ltype: LazyType = innerNamer.typeCompleter(tree); + if (!tparams.isEmpty) { + new Namer(context.makeNewScope(tree, tree.symbol)).enterSyms(tparams); + ltype = new LazyPolyType(tparams map (.symbol), ltype); + skolemize(tparams); + } + tree.symbol.setInfo(ltype); + } + def finish = finishWith(List()); + + + if (tree.symbol == NoSymbol) { + val owner = context.owner; + tree match { + case PackageDef(name, stats) => + tree.symbol = enterPackageSymbol(tree.pos, name); + val namer = new Namer( + context.make(tree, tree.symbol.moduleClass, tree.symbol.info.decls)); + namer.enterSyms(stats); + case ClassDef(mods, name, tparams, _, impl) => + if ((mods.flags & (CASE | ABSTRACT)) == CASE) { // enter case factory method. + tree.symbol = enterCaseFactorySymbol( + tree.pos, mods.flags & AccessFlags | METHOD | CASE, name.toTermName) + .setInfo(innerNamer.caseFactoryCompleter(tree)); + setPrivate(tree.symbol, mods); + } + tree.symbol = enterClassSymbol(tree.pos, mods.flags, name); + setPrivate(tree.symbol, mods); + finishWith(tparams); + case ModuleDef(mods, name, _) => + tree.symbol = enterModuleSymbol(tree.pos, mods.flags | MODULE | FINAL, name); + setPrivate(tree.symbol, mods); + setPrivate(tree.symbol.moduleClass, mods); + tree.symbol.moduleClass.setInfo(innerNamer.typeCompleter(tree)); + finish + case ValDef(mods, name, tp, rhs) => + if (context.owner.isClass & (mods.flags & LOCAL) == 0) { + val accflags = + ((if ((mods.flags & MUTABLE) != 0) mods.flags & ~MUTABLE else mods.flags | STABLE) | + (if ((mods.flags & DEFERRED) == 0) ACCESSOR else 0)); + val getter = owner.newMethod(tree.pos, name) + .setFlag(accflags) + .setInfo(innerNamer.getterTypeCompleter(tree)); + setPrivate(getter, mods); + enterInScope(getter); + if ((mods.flags & MUTABLE) != 0) { + val setter = owner.newMethod(tree.pos, nme.getterToSetter(name)) + .setFlag(accflags & ~STABLE & ~CASEACCESSOR) + .setInfo(innerNamer.setterTypeCompleter(tree)); + setPrivate(setter, mods); + enterInScope(setter) + } + tree.symbol = + if ((mods.flags & DEFERRED) == 0) + enterInScope(owner.newValue(tree.pos, nme.getterToLocal(name))) + .setFlag(mods.flags & FieldFlags | PRIVATE | LOCAL) + .setInfo(innerNamer.typeCompleter(tree)) + else getter; + } else { + tree.symbol = enterInScope(owner.newValue(tree.pos, name)) + .setFlag(mods.flags); + finish + } + case DefDef(mods, nme.CONSTRUCTOR, tparams, vparams, tp, rhs) => + tree.symbol = enterInScope(owner.newConstructor(tree.pos)) + .setFlag(mods.flags | owner.getFlag(ConstrFlags)); + setPrivate(tree.symbol, mods); + finishWith(tparams); + case DefDef(mods, name, tparams, _, _, _) => + tree.symbol = enterInScope(owner.newMethod(tree.pos, name)) + .setFlag(mods.flags); + setPrivate(tree.symbol, mods); + finishWith(tparams); + case AbsTypeDef(mods, name, _, _) => + tree.symbol = enterInScope(owner.newAbstractType(tree.pos, name)) + .setFlag(mods.flags); + setPrivate(tree.symbol, mods); + finish + case AliasTypeDef(mods, name, tparams, _) => + tree.symbol = enterInScope(owner.newAliasType(tree.pos, name)) + .setFlag(mods.flags); + setPrivate(tree.symbol, mods); + finishWith(tparams) + case Attributed(attr, defn) => + enterSym(defn); + case DocDef(_, defn) => + enterSym(defn) + case imp @ Import(_, _) => + tree.symbol = NoSymbol.newImport(tree.pos).setInfo(innerNamer.typeCompleter(tree)); + return new Namer(context.makeNewImport(imp)); + case _ => + } + } + this + } + +// --- Lazy Type Assignment -------------------------------------------------- + + val typer = newTyper(context); + + def typeCompleter(tree: Tree) = new TypeCompleter(tree) { + override def complete(sym: Symbol): unit = { + if (settings.debug.value) log("defining " + sym); + val tp = typeSig(tree); + sym.setInfo(tp); + if (settings.Xgadt.value) System.out.println("" + sym + ":" + tp); + if (settings.debug.value) log("defined " + sym); + validate(sym); + } + } + + def getterTypeCompleter(tree: Tree) = new TypeCompleter(tree) { + override def complete(sym: Symbol): unit = { + if (settings.debug.value) log("defining " + sym); + sym.setInfo(PolyType(List(), typeSig(tree))); + if (settings.debug.value) log("defined " + sym); + validate(sym); + } + } + + def setterTypeCompleter(tree: Tree) = new TypeCompleter(tree) { + override def complete(sym: Symbol): unit = { + if (settings.debug.value) log("defining " + sym); + sym.setInfo(MethodType(List(typeSig(tree)), UnitClass.tpe)); + if (settings.debug.value) log("defined " + sym); + validate(sym); + } + } + + def selfTypeCompleter(tree: Tree) = new TypeCompleter(tree) { + override def complete(sym: Symbol): unit = { + sym.setInfo(typer.typedType(tree).tpe); + } + } + + def caseFactoryCompleter(tree: Tree) = new TypeCompleter(tree) { + override def complete(sym: Symbol): unit = { + val clazz = tree.symbol; + var tpe = clazz.primaryConstructor.tpe; + val tparams = clazz.unsafeTypeParams; + if (!tparams.isEmpty) tpe = PolyType(tparams, tpe).cloneInfo(sym); + sym.setInfo(tpe); + } + } + + private def deconstIfNotFinal(sym: Symbol, tpe: Type): Type = + if (sym.isVariable || + !(sym hasFlag FINAL) || + sym.isMethod && !(sym hasFlag ACCESSOR)) tpe.deconst + else tpe; + + def enterValueParams(owner: Symbol, vparamss: List[List[ValDef]]): List[List[Symbol]] = { + def enterValueParam(param: ValDef): Symbol = { + param.symbol = owner.newValueParameter(param.pos, param.name) + .setInfo(typeCompleter(param)) + .setFlag(param.mods.flags & (BYNAMEPARAM | IMPLICIT)); + setPrivate(param.symbol, param.mods); + context.scope enter param.symbol; + param.symbol + } + vparamss.map(.map(enterValueParam)) + } + + /** A creator for polytypes. If tparams is empty, simply returns result type */ + private def makePolyType(tparams: List[Symbol], tpe: Type): Type = + if (tparams.isEmpty) tpe + else + PolyType(tparams, tpe match { + case PolyType(List(), tpe1) => tpe1 + case _ => tpe + }); + + private def templateSig(templ: Template): Type = { + val clazz = context.owner; + val parents = typer.parentTypes(templ) map (p => if (p.tpe.isError) AnyRefClass.tpe else p.tpe); + val decls = new Scope(); + log("members of " + clazz + "=" + decls.hashCode());//debug + new Namer(context.make(templ, clazz, decls)).enterSyms(templ.body); + ClassInfoType(parents, decls, clazz) + } + + private def classSig(tparams: List[AbsTypeDef], tpt: Tree, impl: Template): Type = { + val tparamSyms = typer.reenterTypeParams(tparams); + if (!tpt.isEmpty) + context.owner.typeOfThis = selfTypeCompleter(tpt); + else tpt.tpe = NoType; + makePolyType(tparamSyms, templateSig(impl)) + } + + private def methodSig(tparams: List[AbsTypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): Type = { + val meth = context.owner; + val tparamSyms = typer.reenterTypeParams(tparams); + val vparamSymss = enterValueParams(meth, vparamss); + val restype = + if (tpt.isEmpty) { + tpt.tpe = if (meth.name == nme.CONSTRUCTOR) context.enclClass.owner.tpe + else deconstIfNotFinal(meth, typer.computeType(rhs)); + tpt.tpe + } else typer.typedType(tpt).tpe; + def mkMethodType(vparams: List[Symbol], restpe: Type) = { + val formals = vparams map (.tpe); + if (!vparams.isEmpty && vparams.head.hasFlag(IMPLICIT)) ImplicitMethodType(formals, restpe) + else MethodType(formals, restpe); + } + makePolyType( + tparamSyms, + if (vparamSymss.isEmpty) PolyType(List(), restype) + else (vparamSymss :\ restype)(mkMethodType)) + } + + /** If `sym' is an implicit value, check that its type signature `tp' is contractive. + * This means: The type of every implicit parameter is properly contained + * in the type that is obtained by removing all implicit parameters and converting + * the rest to a function type. + * If the check succeeds return `tp' itself, otherwise `ErrorType'. + */ + private def checkContractive(sym: Symbol, tp: Type): Type = { + /* The type signature without implicit parameters converted to function type */ + def provided(tp: Type): Type = tp match { + case PolyType(_, restpe) => provided(restpe); + case mt: ImplicitMethodType => mt.resultType; + case MethodType(formals, restpe) => functionType(formals, provided(restpe)) + case _ => tp + } + /* The types of all implicit parameters */ + def required(tp: Type): List[Type] = tp match { + case PolyType(_, restpe) => required(restpe); + case mt: ImplicitMethodType => mt.paramTypes; + case MethodType(formals, restpe) => required(restpe); + case _ => List() + } + var result = tp; + if (sym hasFlag IMPLICIT) { + val p = provided(tp); + for (val r <- required(tp)) { + if (!isContainedIn(r, p) || (r =:= p)) { + context.error(sym.pos, "implicit " + sym + " is not contractive," + + "\n because the implicit parameter type " + r + + "\n is not strictly contained in the signature " + p); + result = ErrorType; + } + } + } + result + } + + private def aliasTypeSig(tpsym: Symbol, tparams: List[AbsTypeDef], rhs: Tree): Type = + makePolyType(typer.reenterTypeParams(tparams), typer.typedType(rhs).tpe); + + private def typeSig(tree: Tree): Type = deSkolemize { + try { + val sym: Symbol = tree.symbol; + tree match { + case ClassDef(_, _, tparams, tpt, impl) => + new Namer(context.makeNewScope(tree, sym)).classSig(tparams, tpt, impl) + + case ModuleDef(_, _, impl) => + val clazz = sym.moduleClass; + clazz.setInfo(new Namer(context.make(tree, clazz)).templateSig(impl)); + //clazz.typeOfThis = singleType(sym.owner.thisType, sym); + clazz.tpe; + + case DefDef(_, _, tparams, vparamss, tpt, rhs) => + val result = + new Namer(context.makeNewScope(tree, sym)).methodSig(tparams, vparamss, tpt, rhs); + checkContractive(sym, result) + + case ValDef(_, _, tpt, rhs) => + if (tpt.isEmpty) + if (rhs.isEmpty) { + context.error(tpt.pos, "missing parameter type"); + ErrorType + } else { + tpt.tpe = deconstIfNotFinal(sym, newTyper(context.make(tree, sym)).computeType(rhs)); + tpt.tpe + } + else { + val typer1 = + if (false && sym.hasFlag(PARAM) && sym.owner.isConstructor && !phase.erasedTypes) { + //todo: find out instead why Template contexts can be nested in Template contexts? + var c = context.enclClass; + while (c.tree.isInstanceOf[Template]) c = c.outer; + newTyper(c) + } else typer; + typer1.typedType(tpt).tpe + } + + case AliasTypeDef(_, _, tparams, rhs) => + new Namer(context.makeNewScope(tree, sym)).aliasTypeSig(sym, tparams, rhs) + + case AbsTypeDef(_, _, lo, hi) => + //System.out.println("bounds of " + sym + ":" + sym.tpe + " = " + typer.typedType(hi).tpe); + TypeBounds(typer.typedType(lo).tpe, typer.typedType(hi).tpe); + + case Import(expr, selectors) => + val expr1 = typer.typedQualifier(expr); + val base = expr1.tpe; + typer.checkStable(expr1); + def checkSelectors(selectors: List[Pair[Name, Name]]): unit = selectors match { + case Pair(from, to) :: rest => + if (from != nme.WILDCARD && base != ErrorType && + base.member(from) == NoSymbol && base.member(from.toTypeName) == NoSymbol) + context.error(tree.pos, from.decode + " is not a member of " + expr); + if (from != nme.WILDCARD && (rest.exists (sel => sel._1 == from))) + context.error(tree.pos, from.decode + " is renamed twice"); + if (to != null && to != nme.WILDCARD && (rest exists (sel => sel._2 == to))) + context.error(tree.pos, to.decode + " appears twice as a target of a renaming"); + checkSelectors(rest) + case Nil => + } + checkSelectors(selectors); + ImportType(expr1) + } + } catch { + case ex: TypeError => + //System.out.println("caught " + ex + " in typeSig");//DEBUG + typer.reportTypeError(tree.pos, ex); + ErrorType + } + } + + /** Check that symbol's definition is well-formed. This means: + * - no conflicting modifiers + * - `abstract' modifier only for classes + * - `override' modifier never for classes + * - `def' modifier never for parameters of case classes + * - declarations only in traits or abstract classes + */ + def validate(sym: Symbol): unit = { + def checkNoConflict(flag1: int, flag2: int): unit = + if (sym.hasFlag(flag1) && sym.hasFlag(flag2)) + context.error(sym.pos, + if (flag1 == DEFERRED) + "abstract member may not have " + Flags.flagsToString(flag2) + " modifier"; + else + "illegal combination of modifiers: " + + Flags.flagsToString(flag1) + " and " + Flags.flagsToString(flag2)); + if (sym.hasFlag(IMPLICIT) && !sym.isTerm) + context.error(sym.pos, "`implicit' modifier can be used only for values, variables and methods"); + if (sym.hasFlag(ABSTRACT) && !sym.isClass) + context.error(sym.pos, "`abstract' modifier can be used only for classes; " + + "\nit should be omitted for abstract members"); + if (sym.hasFlag(OVERRIDE | ABSOVERRIDE) && sym.isClass) + context.error(sym.pos, "`override' modifier not allowed for classes"); + if (sym.hasFlag(ABSOVERRIDE) && !sym.owner.isTrait) + context.error(sym.pos, "`abstract override' modifier only allowed for members of traits"); + if (sym.info.symbol == FunctionClass(0) && + sym.isValueParameter && sym.owner.isClass && sym.owner.hasFlag(CASE)) + context.error(sym.pos, "pass-by-name arguments not allowed for case class parameters"); + if ((sym.flags & DEFERRED) != 0) { + if (!sym.isValueParameter && !sym.isTypeParameterOrSkolem && + (!sym.owner.isClass || sym.owner.isModuleClass || sym.owner.isAnonymousClass)) { + context.error(sym.pos, + "only classes can have declared but undefined members" + + (if (!sym.isVariable) "" + else "\n(Note that variables need to be initialized to be defined)")); + sym.resetFlag(DEFERRED); + } + } + checkNoConflict(DEFERRED, PRIVATE); + checkNoConflict(FINAL, SEALED); + checkNoConflict(PRIVATE, PROTECTED); + checkNoConflict(PRIVATE, OVERRIDE); + checkNoConflict(DEFERRED, FINAL); + } + } + + /* Is type `tp1' properly contained in type `tp2'? */ + def isContainedIn(tp1: Type, tp2: Type) = { + //System.out.println("is " + tp1 + " contained in " + tp2 + "?");//DEBUG + new ContainsTraverser(tp1).traverse(tp2).result; + } + + /* Type `elemtp' is contained in type `tp' is one of the following holds: + * - elemtp and tp are the same + * - tp is a function type and elemtp is not + * - tp and elemtp are function types, and arity of tp is greater than arity of elemtp + * - tp and elemtp are both parameterized types with same type constructor and prefix, + * and each type argument of elemtp is contained in the corresponding type argument of tp. + */ + private class ContainsTraverser(elemtp: Type) extends TypeTraverser { + var result = false; + def traverse(tp: Type): ContainsTraverser = { + if (!result) { + if (elemtp =:= tp) + result = true + else if (isFunctionType(tp) && + (!isFunctionType(elemtp) || tp.typeArgs.length > elemtp.typeArgs.length)) + result = true + else Pair(tp, elemtp) match { + case Pair(TypeRef(pre, sym, args), TypeRef(elempre, elemsym, elemargs)) => + if ((sym == elemsym) && (pre =:= elempre) && (args.length == elemargs.length)) + result = List.forall2(elemargs, args) (isContainedIn) + case _ => + } + } + if (!result) mapOver(tp); + this + } + } + + abstract class TypeCompleter(val tree: Tree) extends LazyType; +} + diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala new file mode 100644 index 0000000000..f60ffae9b5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -0,0 +1,589 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import symtab.Flags._; +import collection.mutable.HashMap; +import transform.InfoTransform; + +/** Post-attribution checking and transformation. + * //todo: check whether we always check type parameter bounds. + * + * This phase performs the following checks. + * + * - All overrides conform to rules. + * - All type arguments conform to bounds. + * - All type variable uses conform to variance annotations. + * - No forward reference to a term symbol extends beyond a value definition. + * + * It performs the following transformations. + * + * - Local modules are replaced by variables and classes + * - Calls to case factory methods are replaced by new's. + * - References to parameter accessors with aliases are replaced by super references to + * these aliases. + */ +abstract class RefChecks extends InfoTransform { + + import global._; + import definitions._; + import typer.{typed, typedOperator, atOwner}; + import posAssigner.atPos; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "refchecks"; + override def phaseNewFlags: long = lateMETHOD; + + def newTransformer(unit: CompilationUnit): RefCheckTransformer = new RefCheckTransformer(unit); + override def changesBaseClasses = false; + + def transformInfo(sym: Symbol, tp: Type): Type = { + if (sym.isModule && !sym.isStatic) { + sym setFlag (lateMETHOD | STABLE); + PolyType(List(), tp) + } else tp + } + + // var m$: T = null; or, if class member: local var m$: T = _; + def newModuleVarDef(accessor: Symbol) = { + val mvar = accessor.owner.newVariable(accessor.pos, nme.moduleVarName(accessor.name)) + .setInfo(accessor.tpe.finalResultType); + if (mvar.owner.isClass) { + mvar setFlag (PRIVATE | LOCAL | SYNTHETIC); + mvar.owner.info.decls.enter(mvar); + } + ValDef(mvar, if (mvar.owner.isClass) EmptyTree else Literal(Constant(null))) + } + + // def m: T = { if (m$ == null) m$ = new m$class; m$ } + def newModuleAccessDef(accessor: Symbol, mvar: Symbol) = { + var mvarRef = if (mvar.owner.isClass) Select(This(mvar.owner), mvar) else Ident(mvar); + DefDef(accessor, vparamss => + Block( + List( + If( + Apply(Select(mvarRef, nme.eq), List(Literal(Constant(null)))), + Assign(mvarRef, + New(TypeTree(mvar.tpe), + List(for (val pt <- mvar.tpe.symbol.primaryConstructor.info.paramTypes) + yield This(accessor.owner.enclClass)))),//??? + EmptyTree)), + mvarRef)) + } + + // def m: T; + def newModuleAccessDcl(accessor: Symbol) = + DefDef(accessor setFlag lateDEFERRED, vparamss => EmptyTree); + + class RefCheckTransformer(unit: CompilationUnit) extends Transformer { + + var localTyper: analyzer.Typer = typer; + +// 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 an immutable value, then so is M. + * 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 + * either M is an abstract type, and M's bounds are sharper than O's bounds. + * or M is an unparameterized type alias or class which conforms to O's bounds. + * 1.8. If O and M are values, then M's type is a subtype of O's type. + * 2. Check that only abstract classes have deferred members + * 3. Check that every member with an `override' modifier + * overrides some other member. + */ + private def checkAllOverrides(clazz: Symbol): unit = { + + val self = clazz.thisType; + + def infoString(sym: Symbol) = ( + sym.toString() + + (if (sym.owner == clazz) "" + else (sym.locationString + + (if (sym.isAliasType) ", which equals " + self.memberInfo(sym) + else if (sym.isAbstractType) " with bounds " + self.memberInfo(sym) + else if (sym.isTerm) " of type " + self.memberInfo(sym) + else ""))) + ); + + /* Check that all conditions for overriding `other' by `member' are met. */ + def checkOverride(clazz: Symbol, member: Symbol, other: Symbol): unit = { + val pos = if (member.owner == clazz) member.pos else clazz.pos; + + def overrideError(msg: String): unit = + if (other.tpe != ErrorType && member.tpe != ErrorType) + unit.error(pos, "error overriding " + infoString(other) + + ";\n " + infoString(member) + " " + msg); + + def overrideTypeError(): unit = { + if (other.tpe != ErrorType && member.tpe != ErrorType) { + overrideError("has incompatible type"); + explainTypes(member.tpe, other.tpe); + } + } + + //System.out.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG + + // return if we already checked this combination elsewhere + if (member.owner != clazz) { + if ((member.owner isSubClass other.owner) && + ((member hasFlag DEFERRED) || !(other hasFlag DEFERRED))) { + //System.out.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG + return; + } + if (clazz.info.parents exists (parent => + (parent.symbol isSubClass other.owner) && (parent.symbol isSubClass member.owner) && + ((member hasFlag DEFERRED) || !(other hasFlag DEFERRED)))) { + //System.out.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG + return; + } + if (clazz.info.parents forall (parent => + (parent.symbol isSubClass other.owner) == (parent.symbol isSubClass member.owner))) { + //System.out.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG + return; + } + } + + if (member hasFlag PRIVATE) { // (1.1) + overrideError("has weaker access privileges; it should not be private"); + } else if ((member hasFlag PROTECTED) && !(other hasFlag PROTECTED)) { // 1 + overrideError("has weaker access privileges; it should not be protected"); + } else if (other hasFlag FINAL) { // (1.2) + overrideError("cannot override final member"); + } else if (!(other hasFlag DEFERRED) && !(member hasFlag (OVERRIDE | ABSOVERRIDE))) { // (1.3) + overrideError("needs `override' modifier"); + } else if ((other hasFlag ABSOVERRIDE) && other.isIncompleteIn(clazz) && !(member hasFlag ABSOVERRIDE)) { + overrideError("needs `abstract override' modifiers"); + } else if (other.isStable && !member.isStable) { // (1.4) + overrideError("needs to be an immutable value"); + } else { + if (other.isAliasType) { + if (!member.typeParams.isEmpty) // (1.5) + overrideError("may not be parameterized"); + if (!other.typeParams.isEmpty) // (1.5) + overrideError("may not override parameterized type"); + if (!(self.memberType(member) =:= self.memberType(other))) // (1.6) + overrideTypeError(); + } else if (other.isAbstractType) { + if (!member.typeParams.isEmpty) // (1.7) + overrideError("may not be parameterized"); + if (!(self.memberInfo(other).bounds containsType self.memberType(member))) // (1.7) + overrideTypeError(); + } else if (other.isTerm) { + if (!(self.memberInfo(member) <:< (self.memberInfo(other)))) // 8 + overrideTypeError(); + } + } + } + + val opc = new overridingPairs.Cursor(clazz); + while (opc.hasNext) { + //System.out.println("overrides " + opc.overriding/* + ":" + opc.overriding.tpe*/ + opc.overriding.locationString + " " + opc.overridden/* + ":" + opc.overridden.tpe*/ + opc.overridden.locationString + opc.overridden.hasFlag(DEFERRED));//DEBUG + if (!opc.overridden.isClass) checkOverride(clazz, opc.overriding, opc.overridden); + + opc.next + } +/* + // 1. Check all members for overriding conditions. + for (val bc <- clazz.info.baseClasses.tail; val other <- bc.info.decls.toList) + if (!other.isClass && !(other hasFlag PRIVATE) && !other.isConstructor) { + val member = clazz.tpe.member(other.name) filter + (sym => sym.owner != other.owner && + (sym.isType || (self.memberType(sym) matches self.memberType(other)))); + if (member hasFlag OVERLOADED) { + val alt1 = member.alternatives.head; + val alt2 = member.alternatives.tail.head; + val pos = if (alt1.owner == clazz) alt1.pos + else if (alt2.owner == clazz) alt2.pos + else clazz.pos; + unit.error(pos, + "ambiguous override: both " + infoString(alt1) + + "\n and " + infoString(alt2) + + "\n override " + infoString(other)); + } else if (member != NoSymbol && !(member hasFlag LOCAL)) { + System.out.println("OVERRIDES " + member + member.locationString + " " + other + other.locationString);//debug + checkOverride(clazz, member, other); + } + } +*/ + // 2. Check that only abstract classes have deferred members + if (clazz.isClass && !clazz.isTrait) { + def abstractClassError(mustBeTrait: boolean, msg: String): unit = { + unit.error(clazz.pos, + (if (clazz.isAnonymousClass || clazz.isModuleClass) "object creation impossible" + else if (mustBeTrait) clazz.toString() + " needs to be a trait" + else clazz.toString() + " needs to be abstract") + ", since " + msg); + clazz.setFlag(ABSTRACT); + } + for (val member <- clazz.tpe.members) + if ((member hasFlag DEFERRED) && !(clazz hasFlag ABSTRACT)) { + abstractClassError(false, + infoString(member) + " is not defined" + + (if (member.isVariable || member.hasFlag(ACCESSOR)) + "\n(Note that variables need to be initialized to be defined)" else "")) + } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) { + val other = member.superSymbol(clazz); + abstractClassError(true, + infoString(member) + " is marked `abstract' and `override'" + + (if (other != NoSymbol) + " and overrides incomplete superclass member " + infoString(other) + else "")) + } + } + + // 3. Check that every defined member with an `override' modifier overrides some other member. + for (val member <- clazz.info.decls.toList) + if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) && + (clazz.info.baseClasses.tail forall { + bc => member.matchingSymbol(bc, clazz.thisType) == NoSymbol + })) { + // for (val bc <- clazz.info.baseClasses.tail) System.out.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG + unit.error(member.pos, member.toString() + " overrides nothing"); + member resetFlag OVERRIDE + } + } + + // Basetype Checking -------------------------------------------------------- + + /** 1. Check that later type instances in the base-type sequence + * are subtypes of earlier type instances of the same trait. + * 2. Check that case classes do not inherit from case classes. + * 3. Check that at most one base type is a case-class. + */ + private def validateBaseTypes(clazz: Symbol): unit = { + val seenTypes = new Array[Type](clazz.info.closure.length); + var seenCaseClass = if (clazz hasFlag CASE) clazz else NoSymbol; + + def validateTypes(tps: List[Type], includeSuper: boolean): unit = { + if (!tps.isEmpty) { + for (val tp <- tps.tail.reverse) validateType(tp, false); + if (includeSuper) validateType(tps.head, true); + } + } + + def validateType(tp: Type, includeSuper: boolean): unit = { + val baseClass = tp.symbol; + if (baseClass.isClass) { + val index = clazz.info.closurePos(baseClass); + if (index >= 0) { + if (seenTypes(index) != null && !(seenTypes(index) <:< tp)) + unit.error(clazz.pos, "illegal inheritance;\n " + clazz + + " inherits different type instances of " + baseClass + + ":\n" + tp + " and " + seenTypes(index)); + seenTypes(index) = tp; + // check that case classes do not inherit from case classes + if (baseClass hasFlag CASE) { + if (seenCaseClass != NoSymbol && seenCaseClass != baseClass) + unit.error(clazz.pos, "illegal combination of case " + + seenCaseClass + " and case " + baseClass + " in one object"); + seenCaseClass = baseClass + } + } + validateTypes(tp.parents, includeSuper); + } + } + + validateTypes(clazz.info.parents, true); + } + + // Variance Checking -------------------------------------------------------- + + private val ContraVariance = -1; + private val NoVariance = 0; + private val CoVariance = 1; + private val AnyVariance = 2; + + /** Check variance of type variables in this type + */ + private def validateVariance(base: Symbol, all: Type, variance: int): unit = { + + def varianceString(variance: int): String = + if (variance == 1) "covariant" + else if (variance == -1) "contravariant" + else "invariant"; + + def relativeVariance(tvar: Symbol): int = { + val clazz = tvar.owner; + var sym = base; + var state = CoVariance; + while (sym != clazz && state != AnyVariance) { + //System.out.println("flip: " + sym + " " + sym.isParameter());//DEBUG + if ((sym hasFlag PARAM) && !sym.owner.isConstructor) state = -state; + else if (!sym.owner.isClass) state = AnyVariance; + else if (sym.isAliasType) state = NoVariance; + sym = sym.owner; + } + state + } + + def validateVariance(tp: Type, variance: int): unit = tp match { + case ErrorType => ; + case WildcardType => ; + case NoType => ; + case NoPrefix => ; + case ThisType(_) => ; + case ConstantType(_) => ; + case SingleType(pre, sym) => + validateVariance(pre, variance) + case TypeRef(pre, sym, args) => + if (sym.variance != NoVariance) { + val v = relativeVariance(sym); + if (v != AnyVariance && sym.variance != v * variance) { + //System.out.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG + unit.error(base.pos, + varianceString(sym.variance) + " " + sym + + " occurs in " + varianceString(v * variance) + + " position in type " + all + " of " + base); + } + } + validateVariance(pre, variance); + validateVarianceArgs(args, variance, sym.typeParams); + case ClassInfoType(parents, decls, symbol) => + validateVariances(parents, variance); + case RefinedType(parents, decls) => + validateVariances(parents, variance); + case TypeBounds(lo, hi) => + validateVariance(lo, -variance); + validateVariance(hi, variance); + case MethodType(formals, result) => + validateVariance(result, variance); + case PolyType(tparams, result) => + validateVariance(result, variance); + } + + def validateVariances(tps: List[Type], variance: int): unit = + tps foreach (tp => validateVariance(tp, variance)); + + def validateVarianceArgs(tps: List[Type], variance: int, tparams: List[Symbol]): unit = + (tps zip tparams) foreach { + case Pair(tp, tparam) => validateVariance(tp, variance * tparam.variance) + } + + validateVariance(all, variance) + } + +// Forward reference checking --------------------------------------------------- + + class LevelInfo(val outer: LevelInfo) { + val scope: Scope = if (outer == null) new Scope() else new Scope(outer.scope); + var maxindex: int = Integer.MIN_VALUE; + var refpos: int = _; + var refsym: Symbol = _; + } + + private var currentLevel: LevelInfo = null; + private val symIndex = new HashMap[Symbol, int]; + + private def pushLevel(): unit = + currentLevel = new LevelInfo(currentLevel); + + private def popLevel(): unit = + currentLevel = currentLevel.outer; + + private def enterSyms(stats: List[Tree]): unit = { + var index = -1; + for (val stat <- stats) { + index = index + 1; + stat match { + case ClassDef(_, _, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) => + assert(stat.symbol != NoSymbol, stat);//debug + if (stat.symbol.isLocal) { + currentLevel.scope.enter(newScopeEntry(stat.symbol, currentLevel.scope)); + symIndex(stat.symbol) = index; + } + case _ => + } + } + } + + private def enterReference(pos: int, sym: Symbol): unit = + if (sym.isLocal) { + val e = currentLevel.scope.lookupEntry(sym.name); + if (e != null && sym == e.sym) { + var l = currentLevel; + while (l.scope != e.owner) l = l.outer; + val symindex = symIndex(sym); + if (l.maxindex < symindex) { + l.refpos = pos; + l.refsym = sym; + l.maxindex = symindex; + } + } + } + +// Transformation ------------------------------------------------------------ + + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { + pushLevel(); + enterSyms(stats); + var index = -1; + val stats1 = stats flatMap { stat => index = index + 1; transformStat(stat, index) } + popLevel(); + stats1 + } + + def transformStat(tree: Tree, index: int): List[Tree] = tree match { + case ModuleDef(mods, name, impl) => + val sym = tree.symbol; + val cdef = ClassDef(mods | MODULE, name, List(), EmptyTree, impl) + .setPos(tree.pos) + .setSymbol(sym.moduleClass) + .setType(NoType); + if (sym.isStatic) List(transform(cdef)) + else { + val vdef = + localTyper.typed { + atPos(tree.pos) { + newModuleVarDef(sym) + } + } + + val ddef = + atPhase(phase.next) { + localTyper.typed { + if (sym.owner.isTrait) newModuleAccessDcl(sym) + else newModuleAccessDef(sym, vdef.symbol) + } + } + + if (sym.owner.isTrait) transformTrees(List(cdef, ddef)) + else transformTrees(List(cdef, vdef, ddef)) + } + + case ValDef(_, _, _, _) => + val tree1 = transform(tree); // important to do before forward reference check + if (tree.symbol.isLocal && index <= currentLevel.maxindex) { + if (settings.debug.value) System.out.println(currentLevel.refsym); + unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol); + } + List(tree1) + + case Import(_, _) => + List() + + case _ => + List(transform(tree)) + } + + override def transform(tree: Tree): Tree = try { + + /* Convert a reference of a case factory to a new of the class it produces. */ + def toConstructor: Tree = { + var tpe = tree.tpe; + while (!tpe.symbol.isClass) tpe = tpe.resultType; + assert(tpe.symbol hasFlag CASE); + typedOperator(atPos(tree.pos)(Select(New(TypeTree(tpe)), tpe.symbol.primaryConstructor))); + } + + /* Check whether argument types conform to bounds of type parameters */ + def checkBounds(tparams: List[Symbol], argtps: List[Type]): unit = try { + typer.infer.checkBounds(tree.pos, tparams, argtps, ""); + } catch { + case ex: TypeError => unit.error(tree.pos, ex.getMessage()); + } + + val savedLocalTyper = localTyper; + val sym = tree.symbol; + var result = tree; + tree match { + case ClassDef(mods, name, tparams, tpe, impl) => + validateVariance(sym, sym.info, CoVariance); + validateVariance(sym, sym.typeOfThis, CoVariance); + + case DefDef(_, _, _, _, _, _) => + validateVariance(sym, sym.tpe, CoVariance); + + case ValDef(_, _, _, _) => + validateVariance(sym, sym.tpe, if (sym.isVariable) NoVariance else CoVariance); + + case AbsTypeDef(_, _, _, _) => + validateVariance(sym, sym.info, CoVariance); + + case AliasTypeDef(_, _, _, _) => + validateVariance(sym, sym.info, CoVariance); + + case Template(_, _) => + localTyper = localTyper.atOwner(tree, currentOwner); + validateBaseTypes(currentOwner); + checkAllOverrides(currentOwner); + + case TypeTree() => + new TypeTraverser { + def traverse(tp: Type) = tp match { + case TypeRef(pre, sym, args) => checkBounds(sym.typeParams, args); this + case _ => this + } + } traverse tree.tpe + + case TypeApply(fn, args) => + checkBounds(fn.tpe.typeParams, args map (.tpe)); + if (sym.isSourceMethod && sym.hasFlag(CASE)) result = toConstructor; + + case New(tpt) => + enterReference(tree.pos, tpt.tpe.symbol); + + case Ident(name) => + if (sym.isSourceMethod && sym.hasFlag(CASE)) + result = toConstructor + else if (name != nme.WILDCARD && name != nme.WILDCARD_STAR.toTypeName) { + assert(sym != NoSymbol, tree);//debug + enterReference(tree.pos, sym); + } + + case Select(qual, name) => + if (sym.isSourceMethod && sym.hasFlag(CASE)) + result = toConstructor + else qual match { + case Super(qualifier, mixin) => + val base = currentOwner.enclClass; + if (sym hasFlag DEFERRED) { + val member = sym.overridingSymbol(base);//??? + if (mixin != nme.EMPTY.toTypeName || member == NoSymbol || + !((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(base))) + unit.error(tree.pos, "symbol accessed from super may not be abstract"); + } + //System.out.println("super: " + tree + " in " + base);//DEBUG + if (base.isTrait && sym.isTerm && mixin == nme.EMPTY.toTypeName) { + val superAccName = nme.superName(sym.name); + val superAcc = base.info.decl(superAccName) suchThat (.alias.==(sym)); + assert(superAcc != NoSymbol, "" + sym + " " + base + " " + superAccName);//debug + val tree1 = Select(This(base), superAcc); + if (settings.debug.value) log("super-replacement: " + tree + "=>" + tree1); + result = atPos(tree.pos) { + Select(gen.This(base), superAcc) setType superAcc.tpe + } + } + case This(_) => + if ((sym hasFlag PARAMACCESSOR) && (sym.alias != NoSymbol)) { + result = typed { + Select( + Super(qual.symbol, qual.symbol.info.parents.head.symbol.name) setPos qual.pos, + sym.alias) setPos tree.pos + } + if (settings.debug.value) + System.out.println("alias replacement: " + tree + " ==> " + result);//debug + } + case _ => + } + case _ => + } + result = super.transform(result); + localTyper = savedLocalTyper; + result + } catch { + case ex: TypeError => + if (settings.debug.value) ex.printStackTrace(); + unit.error(tree.pos, ex.getMessage()); + tree + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala new file mode 100644 index 0000000000..50749deb5e --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -0,0 +1,89 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import nsc.util.ListBuffer; +import nsc.symtab.Flags._; + +/** A sample transform. + */ +abstract class SuperAccessors extends transform.Transform { + // inherits abstract value `global' and class `Phase' from Transform + + import global._; + import posAssigner.atPos; + import typer.typed; + + /** the following two members override abstract members in Transform */ + val phaseName: String = "superaccessors"; + + protected def newTransformer(unit: CompilationUnit): Transformer = new SuperAccTransformer; + + class SuperAccTransformer extends Transformer { + private var validCurrentOwner = true; + private var accDefs: List[Pair[Symbol, ListBuffer[Tree]]] = List(); + + private def accDefBuf(clazz: Symbol) = accDefs.dropWhile(._1.!=(clazz)).head._2; + + private def transformArgs(args: List[Tree], formals: List[Type]) = { + if (!formals.isEmpty && formals.last.symbol == definitions.ByNameParamClass) + ((args take (formals.length - 1) map transform) ::: + withInvalidOwner { args drop (formals.length - 1) map transform }) + else + args map transform + } + + override def transform(tree: Tree): Tree = tree match { + case Template(parents, body) => + val ownAccDefs = new ListBuffer[Tree]; + accDefs = Pair(currentOwner, ownAccDefs) :: accDefs; + val body1 = transformTrees(body); + accDefs = accDefs.tail; + copy.Template(tree, parents, ownAccDefs.toList ::: body1); + case Select(sup @ Super(_, mix), name) => + val clazz = sup.symbol; + if (tree.isTerm && mix == nme.EMPTY.toTypeName && + (clazz.isTrait || clazz != currentOwner.enclClass || !validCurrentOwner)) { + val supername = nme.superName(tree.symbol.name); + var superAcc = clazz.info.decl(supername).suchThat(.alias.==(tree.symbol)); + if (superAcc == NoSymbol) { + if (settings.debug.value) log("add super acc " + tree.symbol + tree.symbol.locationString + " to `" + clazz);//debug + superAcc = + clazz.newMethod(tree.pos, supername) + .setFlag(SUPERACCESSOR | PRIVATE) + .setAlias(tree.symbol) + .setInfo(clazz.thisType.memberType(tree.symbol)); + clazz.info.decls enter superAcc; + accDefBuf(clazz) += typed(DefDef(superAcc, vparamss => EmptyTree)) + } + atPos(sup.pos) { + Select(gen.This(clazz), superAcc) setType tree.tpe; + } + } else tree + case Apply(fn, args) => + copy.Apply(tree, transform(fn), transformArgs(args, fn.tpe.paramTypes)) + case Function(vparams, body) => + withInvalidOwner { + copy.Function(tree, vparams, transform(body)) + } + case _ => + super.transform(tree) + } + + override def atOwner[A](owner: Symbol)(trans: => A): A = { + if (owner.isClass) validCurrentOwner = true; + super.atOwner(owner)(trans) + } + + private def withInvalidOwner[A](trans: => A): A = { + val prevValidCurrentOwner = validCurrentOwner; + validCurrentOwner = false; + val result = trans; + validCurrentOwner = prevValidCurrentOwner; + result + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala new file mode 100644 index 0000000000..da6ac07d6c --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -0,0 +1,133 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import symtab.Flags._; +import util.ListBuffer; + +/** + * - caseArity, caseElement implementations added to case classes + * - equals, and hashCode and toString methods are added to case classes, + * unless they are defined in the class or a baseclass + * different from java.lang.Object + * - toString method is added to case objects, + * unless they are defined in the class or a baseclass + * different from java.lang.Object +*/ +[_trait_] abstract class SyntheticMethods: Analyzer { + import global._; // the global environment + import definitions._; // standard classes and methods + import typer.{typed}; // methods to type trees + + def addSyntheticMethods(templ: Template, clazz: Symbol): Template = { + + def hasImplementation(name: Name): boolean = { + val sym = clazz.info.nonPrivateMember(name); + (sym.isTerm && + (sym.owner == clazz || + !(ObjectClass isSubClass sym.owner) && !(sym hasFlag DEFERRED))) + } + + def syntheticMethod(name: Name, flags: int, tpe: Type) = + newSyntheticMethod(name, flags | OVERRIDE, tpe); + + def newSyntheticMethod(name: Name, flags: int, tpe: Type) = { + val method = clazz.newMethod(clazz.pos, name) setFlag (flags) setInfo tpe; + clazz.info.decls.enter(method); + method + } + + def caseElementMethod: Tree = { + val method = syntheticMethod( + nme.caseElement, FINAL, MethodType(List(IntClass.tpe), AnyClass.tpe)); + val caseFields = clazz.caseFieldAccessors map gen.mkRef; + typed( + DefDef(method, vparamss => + if (caseFields.isEmpty) Literal(Constant(null)) + else { + var i = caseFields.length; + var cases = List(CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(null)))); + for (val field <- caseFields.reverse) { + i = i - 1; cases = CaseDef(Literal(Constant(i)), EmptyTree, field) :: cases + } + Match(Ident(vparamss.head.head), cases) + })) + } + + def caseArityMethod: Tree = { + val method = syntheticMethod(nme.caseArity, FINAL, PolyType(List(), IntClass.tpe)); + typed(DefDef(method, vparamss => Literal(Constant(clazz.caseFieldAccessors.length)))) + } + + def caseNameMethod: Tree = { + val method = syntheticMethod(nme.caseName, FINAL, PolyType(List(), StringClass.tpe)); + typed(DefDef(method, vparamss => Literal(Constant(clazz.name.decode)))) + } + + def moduleToStringMethod: Tree = { + val method = syntheticMethod(nme.toString_, FINAL, MethodType(List(), StringClass.tpe)); + typed(DefDef(method, vparamss => Literal(Constant(clazz.name.decode)))) + } + + def tagMethod: Tree = { + val method = syntheticMethod(nme.tag, FINAL, MethodType(List(), IntClass.tpe)); + typed(DefDef(method, vparamss => Literal(Constant(clazz.tag)))) + } + + def forwardingMethod(name: Name): Tree = { + val target = getMember(ScalaRunTimeModule, "_" + name); + val method = syntheticMethod( + name, 0, MethodType(target.tpe.paramTypes.tail, target.tpe.resultType)); + typed(DefDef(method, vparamss => + Apply(gen.mkRef(target), This(clazz) :: (vparamss.head map Ident)))); + } + + val SerializableAttr = definitions.SerializableAttr; + + def isSerializable(clazz: Symbol): Boolean = { + clazz.attributes.exists(p => p match { + case Pair(SerializableAttr, _) => true; + case _ => false + }) + } + + def readResolveMethod: Tree = { + // !!! the synthetic method "readResolve" should be private, + // but then it is renamed !!! + val method = newSyntheticMethod(nme.readResolve, PROTECTED, + MethodType(List(), ObjectClass.tpe)); + typed(DefDef(method, vparamss => gen.mkRef(clazz.sourceModule))) + } + + val ts = new ListBuffer[Tree]; + if ((clazz hasFlag CASE) && !phase.erasedTypes) { + // case classes are implicitly declared serializable + clazz.attributes = Pair(SerializableAttr, List()) :: clazz.attributes; + + ts += tagMethod; + if (clazz.isModuleClass) { + if (!hasImplementation(nme.toString_)) ts += moduleToStringMethod; + } else { + if (!hasImplementation(nme.hashCode_)) ts += forwardingMethod(nme.hashCode_); + if (!hasImplementation(nme.toString_)) ts += forwardingMethod(nme.toString_); + if (!hasImplementation(nme.equals_)) ts += forwardingMethod(nme.equals_); + } + if (!hasImplementation(nme.caseElement)) ts += caseElementMethod; + if (!hasImplementation(nme.caseArity)) ts += caseArityMethod; + if (!hasImplementation(nme.caseName)) ts += caseNameMethod; + } + if (!phase.erasedTypes && clazz.isModuleClass && isSerializable(clazz)) { + // If you serialize a singleton and then deserialize it twice, + // you will have two instances of your singleton, unless you implement + // the readResolve() method (see http://www.javaworld.com/javaworld/ + // jw-04-2003/jw-0425-designpatterns_p.html) + if (!hasImplementation(nme.readResolve)) ts += readResolveMethod; + } + val synthetics = ts.toList; + copy.Template( + templ, templ.parents, if (synthetics.isEmpty) templ.body else templ.body ::: synthetics) + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala new file mode 100644 index 0000000000..b494e1c33f --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -0,0 +1,139 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import scala.tools.nsc.util.Position; +import scala.tools.nsc.reporters.AbstractReporter; +import symtab.Flags._; + +abstract class TreeCheckers extends Analyzer { + + import global._; + + val tpeOfTree = new scala.collection.mutable.HashMap[Tree, Type]; + + def checkTrees: unit = { + System.out.println("[consistency check at start of phase " + phase + "]"); + for (val unit <- currentRun.units) check(unit); + } + + def check(unit: CompilationUnit): unit = { + val areporter = if (reporter.isInstanceOf[AbstractReporter]) reporter.asInstanceOf[AbstractReporter] else null; + + val curPrompt = if (areporter != null) { + val ret = areporter.prompt; + areporter.prompt = true; + ret; + } else false; + + val context = startContext.make(unit); + context.checking = true; + tpeOfTree.clear; + val checker = new TreeChecker(context); + checker.precheck.traverse(unit.body); + checker.typed(unit.body); + checker.postcheck.traverse(unit.body); + if (areporter != null) + areporter.prompt = curPrompt; + } + + override def newTyper(context: Context): Typer = new TreeChecker(context); + + class TreeChecker(context0: Context) extends Typer(context0) { + + import infer._; + + override def typed(tree: Tree, mode: int, pt: Type): Tree = { + //System.out.println("**** checking " + tree);//debug + tree match { + case EmptyTree | TypeTree() => + ; + case _ => + if (!tpeOfTree.contains(tree)) { + tpeOfTree.update(tree, tree.tpe); + tree.tpe = null + } + val newtree = super.typed(tree, mode, pt); + if ((newtree ne tree) && !newtree.isInstanceOf[Literal]) + error(tree.pos, "trees differ\n old: " + tree + " [" + tree.getClass() + "]\n new: " + + newtree + " [" + newtree.getClass() + "]"); + } + tree + } + override def typed(tree: Tree) = super.typed(tree); // doto remove for new compiler + + object precheck extends Traverser { + override def traverse(tree: Tree): unit = + try { + tree match { + case DefDef(_, _, _, _, _, _) => + if (tree.symbol.hasFlag(ACCESSOR) && + !tree.symbol.hasFlag(DEFERRED) && + !tree.symbol.tpe.resultType.isInstanceOf[ConstantType]) { + assert(tree.symbol.accessed != NoSymbol); + assert(tree.symbol.accessed.getter(tree.symbol.owner) == tree.symbol || + tree.symbol.accessed.setter(tree.symbol.owner) == tree.symbol); + } + case ValDef(_, _, _, _) => + if (tree.symbol.hasGetter) { + assert(tree.symbol.getter(tree.symbol.owner) != NoSymbol) + } + case Apply(_, args) => + assert(args forall (EmptyTree !=)) + case Select(_, _) => + assert(tree.symbol != NoSymbol, tree); + case This(_) => + if (!(tree.symbol.isStatic && (tree.symbol hasFlag MODULE))) { + var o = currentOwner; + while (o != tree.symbol) { + o = o.owner; + assert(o != NoSymbol, tree) + } + } + case _ => + } + if (tree.pos == Position.NOPOS && tree != EmptyTree) { + error(tree.pos, "tree without position: " + tree) + } else if (tree.tpe == null && phase.id >= currentRun.typerPhase.id) { + error(tree.pos, "tree without type: " + tree); + } else if (tree.isDef && tree.symbol.owner != currentOwner) { + var owner = currentOwner; + while (owner.isTerm && !owner.isMethod && tree.symbol.owner != owner) + owner = owner.owner; + if (tree.symbol.owner != owner) { + error(tree.pos, "" + tree.symbol + " has wrong owner: " + tree.symbol.owner + + tree.symbol.owner.locationString + ", should be: " + + currentOwner + currentOwner.locationString) + } + } else { + super.traverse(tree) + } + } catch { + case ex: Throwable => + if (settings.debug.value) + System.out.println("exception when traversing " + tree); + throw(ex) + } + } + + object postcheck extends Traverser { + override def traverse(tree: Tree): unit = tree match { + case EmptyTree | TypeTree() => + ; + case _ => + tpeOfTree.get(tree) match { + case Some(oldtpe) => + if (!(oldtpe =:= tree.tpe)) + error(tree.pos, "types differ\n old: " + oldtpe + "\n new: " + tree.tpe + + "\n tree: " + tree); + tree.tpe = oldtpe; + super.traverse(tree) + case None => + } + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala new file mode 100644 index 0000000000..f4098b615a --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -0,0 +1,1567 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +//todo: rewrite or disallow new T where T is a trait (currently: <init> not a member of T) +package scala.tools.nsc.typechecker; + +import nsc.util.ListBuffer; +import symtab.Flags._; +import scala.tools.nsc.util.Position; +import collection.mutable.HashMap; + +/** Methods to create symbols and to enter them into scopes. */ +[_trait_] abstract class Typers: Analyzer { + import global._; + import definitions._; + import posAssigner.atPos; + + var appcnt = 0; + var idcnt = 0; + var selcnt = 0; + var implcnt = 0; + var impltime = 0l; + + private val transformed = new HashMap[Tree, Tree]; + + private val superDefs = new HashMap[Symbol, ListBuffer[Tree]]; + + def resetTyper: unit = { + resetContexts; + transformed.clear; + superDefs.clear; + } + + def newTyper(context: Context): Typer = new Typer(context); + + class Typer(context0: Context) { + import context0.unit; + + val infer = new Inferencer(context0) { + override def isCoercible(tp: Type, pt: Type): boolean = ( + tp.isError || pt.isError || + context0.reportGeneralErrors && // this condition prevents chains of views + inferView(Position.NOPOS, tp, pt, false) != EmptyTree + ) + } + + private def inferView(pos: int, from: Type, to: Type, reportAmbiguous: boolean): Tree = { + if (settings.debug.value) log("infer view from " + from + " to " + to);//debug + if (phase.erasedTypes) EmptyTree + else from match { + case MethodType(_, _) => EmptyTree + case OverloadedType(_, _) => EmptyTree + case PolyType(_, _) => EmptyTree + case _ => inferImplicit(pos, functionType(List(from), to), true, reportAmbiguous) + } + } + + private def inferView(pos: int, from: Type, name: Name, reportAmbiguous: boolean): Tree = { + val to = refinedType(List(WildcardType), NoSymbol); + val psym = (if (name.isTypeName) to.symbol.newAbstractType(pos, name) + else to.symbol.newValue(pos, name)) setInfo WildcardType; + to.decls.enter(psym); + inferView(pos, from, to, reportAmbiguous) + } + + import infer._; + + private var namerCache: Namer = null; + def namer = { + if (namerCache == null || namerCache.context != context) namerCache = new Namer(context); + namerCache + } + + private var context = context0; + + /** Mode constants + */ + val NOmode = 0x000; + val EXPRmode = 0x001; // these 3 modes are mutually exclusive. + val PATTERNmode = 0x002; + val TYPEmode = 0x004; + + val INCONSTRmode = 0x008; // orthogonal to above. When set we are + // in the body of a constructor + + val FUNmode = 0x10; // orthogonal to above. When set + // we are looking for a method or constructor + + val POLYmode = 0x020; // orthogonal to above. When set + // expression types can be polymorphic. + + val QUALmode = 0x040; // orthogonal to above. When set + // expressions may be packages and + // Java statics modules. + + val TAPPmode = 0x080; // Set for the function/type constructor part + // of a type application. When set we do not + // decompose PolyTypes. + + val SUPERCONSTRmode = 0x100; // Set for the `super' in a superclass constructor call + // super.<init> + + private val stickyModes: int = EXPRmode | PATTERNmode | TYPEmode; + + /** Report a type error. + * @param pos The position where to report the error + * @param ex The exception that caused the error */ + def reportTypeError(pos: int, ex: TypeError): unit = { + val msg = ex match { + case CyclicReference(sym, info: TypeCompleter) => + info.tree match { + case ValDef(_, _, tpt, _) if (tpt.tpe == null) => + "recursive " + sym + " needs type" + case DefDef(_, _, _, _, tpt, _) if (tpt.tpe == null) => + "recursive " + sym + " needs result type" + case _ => + ex.getMessage() + } + case _ => + ex.getMessage() + } + if (settings.debug.value) ex.printStackTrace(); + if (context.reportGeneralErrors) error(pos, msg) + else throw new Error(msg) + } + + /** Check that tree is a stable expression. + */ + def checkStable(tree: Tree): Tree = + if (treeInfo.isPureExpr(tree) || tree.tpe.isError) tree; + else errorTree(tree, "stable identifier required, but " + tree + " found."); + + /** Check that type `tp' is not a subtype of itself. + */ + def checkNonCyclic(pos: int, tp: Type): unit = { + def checkNotLocked(sym: Symbol): boolean = { + sym.initialize; + if (sym hasFlag LOCKED) { + error(pos, "cyclic aliasing or subtyping involving " + sym); false + } else true + } + tp match { + case TypeRef(pre, sym, args) => + if (checkNotLocked(sym) && (sym.isAliasType || sym.isAbstractType)) { + //System.out.println("checking " + sym);//DEBUG + checkNonCyclic(pos, pre.memberInfo(sym).subst(sym.typeParams, args), sym); + } + case SingleType(pre, sym) => + checkNotLocked(sym) + case st: SubType => + checkNonCyclic(pos, st.supertype) + case ct: CompoundType => + for (val p <- ct.parents) checkNonCyclic(pos, p) + case _ => + } + } + + def checkNonCyclic(pos: int, tp: Type, lockedSym: Symbol): unit = { + lockedSym.setFlag(LOCKED); + checkNonCyclic(pos, tp); + lockedSym.resetFlag(LOCKED) + } + + /** Check that type of given tree does not contain local or private components + */ + object checkNoEscaping extends TypeMap { + private var owner: Symbol = _; + private var scope: Scope = _; + private var badSymbol: Symbol = _; + + /** Check that type `tree' does not refer to private components unless itself is wrapped + * in something private (`owner' tells where the type occurs). */ + def privates[T <: Tree](owner: Symbol, tree: T): T = { + check(owner, EmptyScope, tree); + } + + /** Check that type `tree' does not refer to entities defined in scope `scope'. */ + def locals[T <: Tree](scope: Scope, pt: Type, tree: T): T = + if (isFullyDefined(pt)) tree setType pt else check(NoSymbol, scope, tree); + + def check[T <: Tree](owner: Symbol, scope: Scope, tree: T): T = { + this.owner = owner; + this.scope = scope; + badSymbol = NoSymbol; + assert(tree.tpe != null, tree);//debug + apply(tree.tpe); + if (badSymbol == NoSymbol) tree + else { + error(tree.pos, + (if (badSymbol.hasFlag(PRIVATE)) "private " else "") + badSymbol + + " escapes its defining scope as part of type " + tree.tpe); + setError(tree) + } + } + + override def apply(t: Type): Type = { + def checkNoEscape(sym: Symbol): unit = { + if (sym.hasFlag(PRIVATE)) { + var o = owner; + while (o != NoSymbol && o != sym.owner && !o.isLocal && !o.hasFlag(PRIVATE)) + o = o.owner; + if (o == sym.owner) badSymbol = sym + } else if (sym.owner.isTerm) { + val e = scope.lookupEntry(sym.name); + if (e != null && e.sym == sym && e.owner == scope && !e.sym.isTypeParameterOrSkolem) + badSymbol = e.sym + } + } + if (badSymbol == NoSymbol) + t match { + case TypeRef(_, sym, _) => checkNoEscape(sym) + case SingleType(_, sym) => checkNoEscape(sym) + case _ => + } + mapOver(t) + } + } + + def reenterValueParams(vparamss: List[List[ValDef]]): unit = + for (val vparams <- vparamss; val vparam <- vparams) context.scope enter vparam.symbol; + + def reenterTypeParams(tparams: List[AbsTypeDef]): List[Symbol] = + for (val tparam <- tparams) yield { + context.scope enter tparam.symbol; + tparam.symbol.deSkolemize + } + + def attrInfo(attr: Tree): AttrInfo = attr match { + case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => + Pair(tpt.tpe, args map { + case Literal(value) => + value + case arg => + error(arg.pos, "attribute argument needs to be a constant; found: " + arg); + null + }) + } + + /** Post-process an identifier or selection node, performing the following: + * (1) Check that non-function pattern expressions are stable + * (2) Check that packages and static modules are not used as values + * (3) Turn tree type into stable type if possible and required by context. */ + private def stabilize(tree: Tree, pre: Type, mode: int, pt: Type): Tree = { + if (tree.symbol.hasFlag(OVERLOADED) && (mode & FUNmode) == 0) + inferExprAlternative(tree, pt); + val sym = tree.symbol; + if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1) + checkStable(tree) + } else if ((mode & (EXPRmode | QUALmode)) == EXPRmode && !sym.isValue) { // (2) + errorTree(tree, sym.toString() + " is not a value"); + } else if (sym.isStable && pre.isStable && tree.tpe.symbol != ByNameParamClass && + (pt.isStable || (mode & QUALmode) != 0 && !sym.isConstant || + sym.isModule && !sym.isMethod)) { + tree.setType(singleType(pre, sym)) + } else tree + } + + def stabilizeFun(tree: Tree, mode: int, pt: Type): Tree = { + val sym = tree.symbol; + val pre = tree match { + case Select(qual, _) => qual.tpe + case _ => NoPrefix + } + if (tree.tpe.isInstanceOf[MethodType] && pre.isStable && + (pt.isStable || (mode & QUALmode) != 0 && !sym.isConstant || sym.isModule)) { + assert(sym.tpe.paramTypes.isEmpty); + tree.setType(MethodType(List(), singleType(pre, sym))) + } else tree + } + + /** Perform the following adaptations of expression, pattern or type `tree' wrt to + * given mode `mode' and given prototype `pt': + * (0) Convert expressions with constant types to literals + * (1) Resolve overloading, unless mode contains FUNmode + * (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, convert to function by eta-expansion, + * except if the method is a constructor, in which case we issue an error. + * (5) Convert a class type that serves as a constructor in a pattern as follows: + * (5.1) If this type refers to a case class, set tree's type to the unique + * instance of its primary constructor that is a subtype of the expected type. + * (5.2) Otherwise, if this type is a subtype of scala.Seq[A], set trees' type + * to a method type from a repeated parameter sequence type A* to the expected type. + * (6) Convert all other types to TypeTree nodes. + * (7) When in TYPEmode nut not FUNmode, check that types are fully parameterized + * (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 a view + * If all this fails, error + */ +// def adapt(tree: Tree, mode: int, pt: Type): Tree = { + protected def adapt(tree: Tree, mode: int, pt: Type): Tree = tree.tpe match { + case ct @ ConstantType(value) if ((mode & TYPEmode) == 0 && (ct <:< pt)) => // (0) + copy.Literal(tree, value) + case OverloadedType(pre, alts) if ((mode & FUNmode) == 0) => // (1) + inferExprAlternative(tree, pt); + adapt(tree, mode, pt) + case PolyType(List(), restpe) => // (2) + adapt(tree setType restpe, mode, pt); + case TypeRef(_, sym, List(arg)) + if ((mode & EXPRmode) != 0 && sym == ByNameParamClass) => // (2) + adapt(tree setType arg, mode, pt); + case PolyType(tparams, restpe) if ((mode & TAPPmode) == 0) => // (3) + val tparams1 = cloneSymbols(tparams); + val tree1 = if (tree.isType) tree + else TypeApply(tree, tparams1 map (tparam => + TypeTree(tparam.tpe) setPos tree.pos)) setPos tree.pos; + context.undetparams = context.undetparams ::: tparams1; + adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt) + case mt: ImplicitMethodType if ((mode & (EXPRmode | FUNmode)) == EXPRmode) => // (4.1) + val tree1 = + if (!context.undetparams.isEmpty & (mode & POLYmode) == 0) { // (9) + val tparams = context.undetparams; + context.undetparams = List(); + inferExprInstance(tree, tparams, pt); + adapt(tree, mode, pt) + } else tree; + typed(applyImplicitArgs(tree1), mode, pt) + case mt: MethodType if ((mode & (EXPRmode | FUNmode)) == EXPRmode && + isCompatible(tree.tpe, pt)) => // (4.2) + if (tree.symbol.isConstructor || pt == WildcardType || + !(pt <:< functionType(mt.paramTypes map (t => WildcardType), WildcardType))) { + errorTree(tree, "missing arguments for " + tree.symbol) //debug + } else { + if (settings.debug.value) log("eta-expanding " + tree + ":" + tree.tpe + " to " + pt);//debug + typed(etaExpand(tree), mode, pt) + } + case _ => + if (tree.isType) { + val clazz = tree.tpe.symbol; + if ((mode & PATTERNmode) != 0) { // (5) + if (tree.tpe.isInstanceOf[MethodType]) { + tree // everything done already + } else { + clazz.initialize; + if (clazz.hasFlag(CASE)) { // (5.1) + val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(tree.tpe.prefix, clazz.owner)) setOriginal(tree); + + + // tree.tpe.prefix.memberType(clazz.primaryConstructor); //!!! + inferConstructorInstance(tree1, clazz.unsafeTypeParams, pt); + tree1 + } else if (clazz.isSubClass(SeqClass)) { // (5.2) + pt.baseType(clazz).baseType(SeqClass) match { + case TypeRef(pre, seqClass, args) => + tree.setType(MethodType(List(typeRef(pre, RepeatedParamClass, args)), pt)) + case NoType => + errorTree(tree, "expected pattern type " + pt + + " does not conform to sequence " + clazz) + } + } else { + if (!tree.tpe.isError) + error(tree.pos, clazz.toString() + " is neither a case class nor a sequence class"); + setError(tree) + } + } + } else if ((mode & FUNmode) != 0) { + tree + } else if (tree.hasSymbol && !tree.symbol.unsafeTypeParams.isEmpty) { // (7) + errorTree(tree, "" + clazz + " takes type parameters"); + } else tree match { // (6) + case TypeTree() => tree + case _ => TypeTree(tree.tpe) setOriginal(tree) + } + } else if ((mode & (EXPRmode | FUNmode)) == (EXPRmode | FUNmode) && + !tree.tpe.isInstanceOf[MethodType] && !tree.tpe.isInstanceOf[OverloadedType] && + ((mode & TAPPmode) == 0 || tree.tpe.typeParams.isEmpty) && + adaptToName(tree, nme.apply).tpe.nonLocalMember(nme.apply) + .filter(m => m.tpe.paramSectionCount > 0) != NoSymbol) { // (8) + typed(atPos(tree.pos)(Select(adaptToName(tree, nme.apply), nme.apply)), mode, pt) + } else if (!context.undetparams.isEmpty & (mode & POLYmode) == 0) { // (9) + val tparams = context.undetparams; + context.undetparams = List(); + inferExprInstance(tree, tparams, pt); + adapt(tree, mode, pt) + } else if (tree.tpe <:< pt) { + tree + } else { + val tree1 = constfold(tree, pt); // (10) (11) + if (tree1.tpe <:< pt) adapt(tree1, mode, pt) + else { + if ((mode & (EXPRmode | FUNmode)) == EXPRmode) { + pt match { + case TypeRef(_, sym, _) => + // note: was if (pt.symbol == UnitClass) but this leads to a potentially + // infinite expansion if pt is constant type () + if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) // (12) + return typed(atPos(tree.pos)(Block(List(tree), Literal(()))), mode, pt) + case _ => + } + if (context.reportGeneralErrors && !tree.tpe.isError && !pt.isError) { + // (13); the condition prevents chains of views + if (settings.debug.value) log("inferring view from " + tree.tpe + " to " + pt); + val coercion = inferView(tree.pos, tree.tpe, pt, true); + if (coercion != EmptyTree) { + if (settings.debug.value) log("inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe); + return typed(Apply(coercion, List(tree)) setPos tree.pos, mode, pt); + } + } + } + if (settings.debug.value) log("error tree = " + tree); + typeErrorTree(tree, tree.tpe, pt) + } + } + } +// System.out.println("adapt " + tree + ":" + tree.tpe + ", mode = " + mode + ", pt = " + pt); +// adapt(tree, mode, pt) +// } + + def adaptToName(qual: Tree, name: Name): Tree = + if (qual.isTerm && (qual.symbol == null || qual.symbol.isValue) && + !phase.erasedTypes && !qual.tpe.widen.isError && + qual.tpe.nonLocalMember(name) == NoSymbol) { + val coercion = inferView(qual.pos, qual.tpe, name, true); + if (coercion != EmptyTree) typedQualifier(atPos(qual.pos)(Apply(coercion, List(qual)))) + else qual + } else qual; + + private def completeSuperType(supertpt: Tree, tparams: List[Symbol], enclTparams: List[Symbol], vparamss: List[List[ValDef]], superargs: List[Tree]): Type = { + enclTparams foreach context.scope.enter; + namer.enterValueParams(context.owner, vparamss); + val newTree = New(supertpt) + .setType(PolyType(tparams, appliedType(supertpt.tpe, tparams map (.tpe)))); + val tree = typed(atPos(supertpt.pos)(Apply(Select(newTree, nme.CONSTRUCTOR), superargs))); + if (settings.debug.value) log("superconstr " + tree + " co = " + context.owner);//debug + tree.tpe + } + + def parentTypes(templ: Template): List[Tree] = try { + if (templ.parents.isEmpty) List() + else { + var supertpt = typedTypeConstructor(templ.parents.head); + var mixins = templ.parents.tail map typedType; + // If first parent is trait, make it first mixin and add its superclass as first parent + while (supertpt.tpe.symbol != null && supertpt.tpe.symbol.initialize.isTrait) { + mixins = typedType(supertpt) :: mixins; + supertpt = TypeTree(supertpt.tpe.parents.head) setPos supertpt.pos; + } + if (supertpt.hasSymbol) { + val tparams = supertpt.symbol.typeParams; + if (!tparams.isEmpty) { + val constr @ DefDef(_, _, _, vparamss, _, Apply(_, superargs)) = + treeInfo.firstConstructor(templ.body); + val outercontext = context.outer; + supertpt = TypeTree( + newTyper(outercontext.makeNewScope(constr, outercontext.owner/*.newValue(templ.pos, newTermName("<dummy>"))*/)) + .completeSuperType( + supertpt, + tparams, + context.owner.unsafeTypeParams, + vparamss map (.map(.duplicate.asInstanceOf[ValDef])), + superargs map (.duplicate))) setPos supertpt.pos; + } + } + //System.out.println("parents(" + context.owner + ") = " + supertpt :: mixins);//DEBUG + List.mapConserve(supertpt :: mixins)(tpt => checkNoEscaping.privates(context.owner, tpt)) + } + } catch { + case ex: TypeError => + reportTypeError(templ.pos, ex); + List(TypeTree(AnyRefClass.tpe)) + } + + /** Check that + * - all parents are class types, + * - first parent cluss is not a trait; following classes are traits, + * - final classes are not inherited, + * - sealed classes are only inherited by classes which are + * nested within definition of base class, or that occur within same + * statement sequence, + * - self-type of current class is a subtype of self-type of each parent class. + * - no two parents define same symbol. + */ + def validateParentClasses(parents: List[Tree], selfType: Type): unit = { + var c = context; + do { c = c.outer } while (c.owner == context.owner); + val defscope = c.scope; + + def validateParentClass(parent: Tree, isFirst: boolean): unit = + if (!parent.tpe.isError) { + val psym = parent.tpe.symbol.initialize; + if (!psym.isClass) + error(parent.pos, "class type expected"); + else if (!isFirst && !psym.isTrait) + error(parent.pos, "" + psym + " is not a trait; cannot be used as mixin"); + else if (psym.hasFlag(FINAL)) + error(parent.pos, "illegal inheritance from final class"); + else if (psym.isSealed && !phase.erasedTypes) { + // are we in same scope as base type definition? + val e = defscope.lookupEntry(psym.name); + if (!(e != null && e.sym == psym && e.owner == defscope)) { + // we are not within same statement sequence + var c = context; + while (c != NoContext && c.owner != psym) c = c.outer.enclClass; + if (c == NoContext) error(parent.pos, "illegal inheritance from sealed " + psym) + } + } + if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes) { + System.out.println(context.owner);//debug + System.out.println(context.owner.unsafeTypeParams);//debug + System.out.println(List.fromArray(context.owner.info.closure));//debug + error(parent.pos, "illegal inheritance;\n self-type " + + selfType + " does not conform to " + parent + + "'s selftype " + parent.tpe.typeOfThis); + if (settings.explaintypes.value) explainTypes(selfType, parent.tpe.typeOfThis); + } + if (parents exists (p => p != parent && p.tpe.symbol == psym && !psym.isError)) + error(parent.pos, "" + psym + " is inherited twice") + } + + if (!parents.isEmpty) { + validateParentClass(parents.head, true); + for (val p <- parents.tail) validateParentClass(p, false); + } + } + + def typedClassDef(cdef: ClassDef): Tree = { + val clazz = cdef.symbol; + reenterTypeParams(cdef.tparams); + val tparams1 = List.mapConserve(cdef.tparams)(typedAbsTypeDef); + val tpt1 = checkNoEscaping.privates(clazz.thisSym, typedType(cdef.tpt)); + val impl1 = newTyper(context.make(cdef.impl, clazz, new Scope())) + .typedTemplate(cdef.impl, parentTypes(cdef.impl)); + val impl2 = addSyntheticMethods(impl1, clazz); + val ret = copy.ClassDef(cdef, cdef.mods, cdef.name, tparams1, tpt1, impl2) + .setType(NoType); + ret; + } + + def typedModuleDef(mdef: ModuleDef): Tree = { + val clazz = mdef.symbol.moduleClass; + val impl1 = newTyper(context.make(mdef.impl, clazz, new Scope())) + .typedTemplate(mdef.impl, parentTypes(mdef.impl)); + val impl2 = addSyntheticMethods(impl1, clazz); + + copy.ModuleDef(mdef, mdef.mods, mdef.name, impl2) setType NoType + } + + def addGetterSetter(stat: Tree): List[Tree] = stat match { + case ValDef(mods, name, tpe, rhs) if !(mods hasFlag LOCAL) && !stat.symbol.isModuleVar => + val vdef = copy.ValDef(stat, mods | PRIVATE | LOCAL, nme.getterToLocal(name), tpe, rhs); + val value = vdef.symbol; + val getter = if (mods hasFlag DEFERRED) value else value.getter(value.owner); + assert(getter != NoSymbol, getter);//debug + val getterDef: DefDef = { + val result = atPos(vdef.pos)( + DefDef(getter, vparamss => + if (mods hasFlag DEFERRED) EmptyTree + else typed(atPos(vdef.pos)(Select(This(value.owner), value)), EXPRmode, value.tpe))); + checkNoEscaping.privates(getter, result.tpt); + result + } + def setterDef: DefDef = { + val setter = value.owner.info.decl(nme.getterToSetter(getter.name)); + assert(setter != NoSymbol, getter);//debug + atPos(vdef.pos)( + DefDef(setter, vparamss => + if (mods hasFlag DEFERRED) EmptyTree + else typed(Assign(Select(This(value.owner), value), + Ident(vparamss.head.head))))) + } + val gs = if (mods hasFlag MUTABLE) List(getterDef, setterDef) + else List(getterDef); + if (mods hasFlag DEFERRED) gs else vdef :: gs + case DocDef(comment, defn) => + addGetterSetter(defn) map (stat => DocDef(comment, stat)) + case Attributed(attr, defn) => + addGetterSetter(defn) map (stat => Attributed(attr.duplicate, stat)) + case _ => + List(stat) + } + + def typedTemplate(templ: Template, parents1: List[Tree]): Template = { + val clazz = context.owner; + if (templ.symbol == NoSymbol) templ setSymbol clazz.newLocalDummy(templ.pos); + val selfType = + if (clazz.isAnonymousClass && !phase.erasedTypes) + intersectionType(clazz.info.parents, clazz.owner) + else if (settings.Xgadt.value) clazz.typeOfThis.asSeenFrom(context.prefix, clazz) + else clazz.typeOfThis; + // the following is necessary for templates generated later + new Namer(context.outer.make(templ, clazz, clazz.info.decls)).enterSyms(templ.body); + validateParentClasses(parents1, selfType); + val body1 = typedStats(templ.body flatMap addGetterSetter, templ.symbol); + copy.Template(templ, parents1, body1) setType clazz.tpe + } + + def typedValDef(vdef: ValDef): ValDef = { + val sym = vdef.symbol; + val typer1 = if (sym.hasFlag(PARAM) && sym.owner.isConstructor) + newTyper(context.makeConstructorContext) + else this; + var tpt1 = checkNoEscaping.privates(sym, typer1.typedType(vdef.tpt)); + checkNonCyclic(vdef.pos, tpt1.tpe, sym); + val rhs1 = + if (vdef.rhs.isEmpty) { + if (sym.isVariable && sym.owner.isTerm && phase.id <= currentRun.typerPhase.id) + error(vdef.pos, "local variables must be initialized"); + vdef.rhs + } else { + newTyper(context.make(vdef, sym)).transformedOrTyped(vdef.rhs, tpt1.tpe) + } + copy.ValDef(vdef, vdef.mods, vdef.name, tpt1, rhs1) setType NoType + } + + /** Enter all aliases of local parameter accessors. */ + def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree): unit = { + if (settings.debug.value) log("computing param aliases for " + clazz + ":" + clazz.primaryConstructor.tpe + ":" + rhs);//debug + def decompose(call: Tree): Pair[Tree, List[Tree]] = call match { + case Apply(fn, args) => + val Pair(superConstr, args1) = decompose(fn); + val formals = fn.tpe.paramTypes; + val args2 = if (formals.isEmpty || formals.last.symbol != RepeatedParamClass) args + else args.take(formals.length - 1) ::: List(EmptyTree); + if (args2.length != formals.length) assert(false, "mismatch " + clazz + " " + formals + " " + args2);//debug + Pair(superConstr, args1 ::: args2) + case Block(stats, expr) => + decompose(stats.head) + case _ => + Pair(call, List()) + } + val Pair(superConstr, superArgs) = decompose(rhs); + assert(superConstr.symbol != null);//debug + if (superConstr.symbol.isPrimaryConstructor) { + val superClazz = superConstr.symbol.owner; + if (!superClazz.hasFlag(JAVA)) { + val superParamAccessors = superClazz.constrParamAccessors; + if (superParamAccessors.length != superArgs.length) { + System.out.println("" + superClazz + ":" + superClazz.info.decls.toList.filter(.hasFlag(PARAMACCESSOR))); + assert(false, "mismatch: " + superParamAccessors + ";" + rhs + ";" + superClazz.info.decls); //debug + } + List.map2(superParamAccessors, superArgs) { (superAcc, superArg) => + superArg match { + case Ident(name) => + if (vparamss.exists(.exists(vp => vp.symbol == superArg.symbol))) { + var alias = superAcc.initialize.alias; + if (alias == NoSymbol) + alias = superAcc.getter(superAcc.owner); + if (alias != NoSymbol && + superClazz.info.nonPrivateMember(alias.name) != alias) + alias = NoSymbol; + if (alias != NoSymbol) { + var ownAcc = clazz.info.decl(name); + if (ownAcc hasFlag ACCESSOR) ownAcc = ownAcc.accessed; + if (settings.debug.value) log("" + ownAcc + " has alias " + alias + alias.locationString);//debug + ownAcc.asInstanceOf[TermSymbol].setAlias(alias) + } + } + case _ => + } + } + () + } + } + } + + def typedSuperCall(tree: Tree): Tree = + typed(tree, EXPRmode | INCONSTRmode, UnitClass.tpe); + + def typedDefDef(ddef: DefDef): DefDef = { + val meth = ddef.symbol; + reenterTypeParams(ddef.tparams); + reenterValueParams(ddef.vparamss); + val tparams1 = List.mapConserve(ddef.tparams)(typedAbsTypeDef); + val vparamss1 = List.mapConserve(ddef.vparamss)(vparams1 => + List.mapConserve(vparams1)(typedValDef)); + for (val vparams <- vparamss1; val vparam <- vparams) { + checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () + } + var tpt1 = + checkNoEscaping.locals(context.scope, WildcardType, + checkNoEscaping.privates(meth, + typedType(ddef.tpt))); + checkNonCyclic(ddef.pos, tpt1.tpe, meth); + val rhs1 = + if (ddef.name == nme.CONSTRUCTOR) { + if (!meth.hasFlag(SYNTHETIC) && + !(meth.owner.isClass || + meth.owner.isModuleClass || + meth.owner.isAnonymousClass || + meth.owner.isRefinementClass)) + error(ddef.pos, "constructor definition not allowed here " + meth.owner);//debug + val result = ddef.rhs match { + case Block(stat :: stats, expr) => + val stat1 = typedSuperCall(stat); + newTyper(context.makeConstructorSuffixContext).typed( + copy.Block(ddef.rhs, stats, expr), UnitClass.tpe) match { + case block1 @ Block(stats1, expr1) => + copy.Block(block1, stat1 :: stats1, expr1) + } + case _ => + typedSuperCall(ddef.rhs) + } + if (meth.isPrimaryConstructor && !phase.erasedTypes && reporter.errors == 0) + computeParamAliases(meth.owner, vparamss1, result); + result + } else transformedOrTyped(ddef.rhs, tpt1.tpe); + copy.DefDef(ddef, ddef.mods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType + } + + def typedAbsTypeDef(tdef: AbsTypeDef): AbsTypeDef = { + val lo1 = checkNoEscaping.privates(tdef.symbol, typedType(tdef.lo)); + val hi1 = checkNoEscaping.privates(tdef.symbol, typedType(tdef.hi)); + checkNonCyclic(tdef.pos, tdef.symbol.tpe); + copy.AbsTypeDef(tdef, tdef.mods, tdef.name, lo1, hi1) setType NoType + } + + def typedAliasTypeDef(tdef: AliasTypeDef): AliasTypeDef = { + reenterTypeParams(tdef.tparams); + val tparams1 = List.mapConserve(tdef.tparams)(typedAbsTypeDef); + val rhs1 = checkNoEscaping.privates(tdef.symbol, typedType(tdef.rhs)); + checkNonCyclic(tdef.pos, tdef.symbol.tpe); + copy.AliasTypeDef(tdef, tdef.mods, tdef.name, tparams1, rhs1) setType NoType + } + + private def enterLabelDef(stat: Tree): unit = stat match { + case ldef @ LabelDef(_, _, _) => + if (ldef.symbol == NoSymbol) + ldef.symbol = namer.enterInScope( + context.owner.newLabel(ldef.pos, ldef.name) setInfo MethodType(List(), UnitClass.tpe)); + case _ => + } + + def typedLabelDef(ldef: LabelDef): LabelDef = { + val restpe = ldef.symbol.tpe.resultType; + val rhs1 = typed(ldef.rhs, restpe); + ldef.params foreach (param => param.tpe = param.symbol.tpe); + copy.LabelDef(ldef, ldef.name, ldef.params, rhs1) setType restpe + } + + def typedBlock(block: Block, mode: int, pt: Type): Block = { + namer.enterSyms(block.stats); + block.stats foreach enterLabelDef; + val stats1 = typedStats(block.stats, context.owner); + val expr1 = typed(block.expr, mode & ~(FUNmode | QUALmode), pt); + val block1 = copy.Block(block, stats1, expr1) + .setType(if (treeInfo.isPureExpr(block)) expr1.tpe else expr1.tpe.deconst); + if (isFullyDefined(pt)) block1 + else { + if (block1.tpe.symbol.isAnonymousClass) + block1 setType intersectionType(block1.tpe.parents, block1.tpe.symbol.owner); + checkNoEscaping.locals(context.scope, pt, block1) + } + } + + def typedCase(cdef: CaseDef, pattpe: Type, pt: Type): CaseDef = { + val pat1: Tree = typedPattern(cdef.pat, pattpe); + val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree + else typed(cdef.guard, BooleanClass.tpe); + var body1: Tree = typed(cdef.body, pt); + if (!context.savedTypeBounds.isEmpty) { + context.restoreTypeBounds; + // the following is a hack to make the pattern matcher work + body1 = + typed { + atPos(body1.pos) { + TypeApply(Select(body1, Any_asInstanceOf), List(TypeTree(pt))) + } + } + } + copy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe + } + + def typedCases(tree: Tree, cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = { + List.mapConserve(cases)(cdef => + newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt)) + } + + def typedFunction(fun: Function, mode: int, pt: Type): Tree = { + def decompose(pt: Type): Triple[Symbol, List[Type], Type] = + if (isFunctionType(pt) + || + pt.symbol == PartialFunctionClass && + fun.vparams.length == 1 && fun.body.isInstanceOf[Match]) + Triple(pt.symbol, pt.typeArgs.init, pt.typeArgs.last) + else + Triple(FunctionClass(fun.vparams.length), fun.vparams map (x => NoType), WildcardType); + + val Triple(clazz, argpts, respt) = + decompose(if (pt.symbol == TypedCodeClass) pt.typeArgs.head else pt); + + val vparamSyms = List.map2(fun.vparams, argpts) { (vparam, argpt) => + if (vparam.tpt.isEmpty) + vparam.tpt.tpe = + if (argpt == NoType) { error(vparam.pos, "missing parameter type"); ErrorType } + else argpt; + namer.enterSym(vparam); + vparam.symbol + } + val vparams = List.mapConserve(fun.vparams)(typedValDef); + for (val vparam <- vparams) { + checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () + } + val body = checkNoEscaping.locals(context.scope, respt, typed(fun.body, respt)); + val formals = vparamSyms map (.tpe); + val restpe = body.tpe.deconst; + val funtpe = typeRef(clazz.tpe.prefix, clazz, formals ::: List(restpe)); + val fun1 = copy.Function(fun, vparams, checkNoEscaping.locals(context.scope, restpe, body)) + .setType(funtpe); + if (pt.symbol == TypedCodeClass) typed(atPos(fun.pos)(codify(fun1))) + else fun1 + } + + def typedRefinement(stats: List[Tree]): List[Tree] = { + namer.enterSyms(stats); + for (val stat <- stats) stat.symbol setFlag OVERRIDE; + typedStats(stats, NoSymbol); + } + + def typedStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = + List.mapConserve(stats) { stat => + if (context.owner.isRefinementClass && !treeInfo.isDeclaration(stat)) + errorTree(stat, "only declarations allowed here"); + stat match { + case imp @ Import(_, _) => + context = context.makeNewImport(imp); + stat.symbol.initialize; + EmptyTree + case _ => + (if (exprOwner != context.owner && (!stat.isDef || stat.isInstanceOf[LabelDef])) + newTyper(context.make(stat, exprOwner)) else this).typed(stat) + } + } + + protected def typed1(tree: Tree, mode: int, pt: Type): Tree = { + + def funmode = mode & stickyModes | FUNmode | POLYmode; + + def ptOrLub(tps: List[Type]) = if (isFullyDefined(pt)) pt else lub(tps); + + def typedTypeApply(fun: Tree, args: List[Tree]): Tree = fun.tpe match { + case OverloadedType(pre, alts) => + inferPolyAlternatives(fun, args.length); + typedTypeApply(fun, args) + case PolyType(tparams, restpe) if (tparams.length != 0) => + if (tparams.length == args.length) { + val targs = args map (.tpe); + checkBounds(tree.pos, tparams, targs, ""); + copy.TypeApply(tree, fun, args) setType restpe.subst(tparams, targs); + } else { + errorTree(tree, "wrong number of type parameters for " + treeSymTypeMsg(fun)) + } + case ErrorType => + setError(tree) + case _ => + System.out.println(fun.toString() + " " + args);//debug + errorTree(tree, treeSymTypeMsg(fun) + " does not take type parameters."); + } + + def typedArg(arg: Tree, pt: Type): Tree = { + val argTyper = if ((mode & INCONSTRmode) != 0) newTyper(context.makeConstructorContext) + else this; + argTyper.typed(arg, mode & stickyModes, pt) + } + + def typedApply(fun: Tree, args: List[Tree]): Tree = fun.tpe match { + case OverloadedType(pre, alts) => + val args1 = List.mapConserve(args)(arg => + typedArg(arg, WildcardType)); + inferMethodAlternative(fun, context.undetparams, args1 map (.tpe.deconst), pt); + typedApply(adapt(fun, funmode, WildcardType), args1); + case MethodType(formals0, restpe) => + val formals = formalTypes(formals0, args.length); + if (formals.length != args.length) { + //System.out.println("" + formals.length + " " + args.length);//DEBUG + errorTree(tree, "wrong number of arguments for " + treeSymTypeMsg(fun)) + } else { + val tparams = context.undetparams; + context.undetparams = List(); + if (tparams.isEmpty) { + val args1 = List.map2(args, formals)(typedArg); + def ifPatternSkipFormals(tp: Type) = tp match { + case MethodType(_, rtp) if ((mode & PATTERNmode) != 0) => rtp + case _ => tp + } + constfold(copy.Apply(tree, fun, args1).setType(ifPatternSkipFormals(restpe))); + } else { + assert((mode & PATTERNmode) == 0); // this case cannot arise for patterns + val lenientTargs = protoTypeArgs(tparams, formals, restpe, pt); + val strictTargs = List.map2(lenientTargs, tparams)((targ, tparam) => + if (targ == WildcardType) tparam.tpe else targ); + def typedArgToPoly(arg: Tree, formal: Type): Tree = { + val lenientPt = formal.subst(tparams, lenientTargs); + val arg1 = typedArg(arg, lenientPt); + val argtparams = context.undetparams; + context.undetparams = List(); + if (!argtparams.isEmpty) { + val strictPt = formal.subst(tparams, strictTargs); + inferArgumentInstance(arg1, argtparams, strictPt, lenientPt); + } + arg1 + } + val args1 = List.map2(args, formals)(typedArgToPoly); + if (args1 exists (.tpe.isError)) setError(tree) + else { + if (settings.debug.value) log("infer method inst " + fun + ", tparams = " + tparams + ", args = " + args1.map(.tpe) + ", pt = " + pt + ", lobounds = " + tparams.map(.tpe.bounds.lo));//debug + val undetparams = inferMethodInstance(fun, tparams, args1, pt); + val result = typedApply(fun, args1); + context.undetparams = undetparams; + result + } + } + } + case ErrorType => + setError(tree) + case _ => + errorTree(tree, "" + fun + " does not take parameters"); + } + + /** The qualifying class of a this or super with prefix `qual' */ + def qualifyingClassContext(qual: Name): Context = { + if (qual == nme.EMPTY.toTypeName) { + if (context.enclClass.owner.isPackageClass) + error(tree.pos, "" + tree + " can be used only in a class, object, or template"); + context.enclClass + } else { + var c = context.enclClass; + while (c != NoContext && c.owner.name != qual) c = c.outer.enclClass; + if (c == NoContext) error(tree.pos, "" + qual + " is not an enclosing class"); + c + } + } + + /** Attribute a selection where `tree' is `qual.name'. + * `qual' is already attributed. + */ + def typedSelect(qual: Tree, name: Name): Tree = { + val sym = + if (tree.symbol != NoSymbol) { + if (phase.erasedTypes && qual.isInstanceOf[Super]) qual.tpe = tree.symbol.owner.tpe; + if (false && settings.debug.value) { // todo: replace by settings.check.value? + val alts = qual.tpe.member(tree.symbol.name).alternatives; + if (!(alts exists (alt => + alt == tree.symbol || alt.isTerm && (alt.tpe matches tree.symbol.tpe)))) + assert(false, "symbol " + tree.symbol + tree.symbol.locationString + " not in " + alts + " of " + qual.tpe + + "\n members = " + qual.tpe.members + + "\n type history = " + qual.tpe.symbol.infosString + + "\n phase = " + phase); + } + tree.symbol + } else qual.tpe match { + case ThisType(clazz) if (clazz == context.enclClass.owner) => + qual.tpe.member(name) + case _ => + qual.tpe.nonLocalMember(name) + } + if (sym == NoSymbol) { + val qual1 = adaptToName(qual, name); + if (qual1 ne qual) return typed(copy.Select(tree, qual1, name), mode, pt) + } + if (sym.info == NoType) { + if (settings.debug.value) log("qual = " + qual + ":" + qual.tpe + "\nSymbol=" + qual.tpe.symbol + "\nsymbol-info = " + qual.tpe.symbol.info + "\nscope-id = " + qual.tpe.symbol.info.decls.hashCode() + "\nmembers = " + qual.tpe.members + "\nfound = " + sym); + if (!qual.tpe.widen.isError) + error(tree.pos, + decode(name) + " is not a member of " + qual.tpe.widen + + (if (Position.line(context.unit.source, qual.pos) < + Position.line(context.unit.source, tree.pos)) + "\npossible cause: maybe a semicolon is missing before `" + name + "'?" else "")); + setError(tree) + } else { + val tree1 = tree match { + case Select(_, _) => copy.Select(tree, qual, name) + case SelectFromTypeTree(_, _) => copy.SelectFromTypeTree(tree, qual, name); + } + stabilize(checkAccessible(tree1, sym, qual.tpe, qual), qual.tpe, mode, pt); + } + } + + /** Attribute an identifier consisting of a simple name or an outer reference. + * @param tree The tree representing the identifier. + * @param name The name of the identifier. + * Transformations: (1) Prefix class members with this. + * (2) Change imported symbols to selections + */ + def typedIdent(name: Name): Tree = { + def ambiguousError(msg: String) = + error(tree.pos, "reference to " + name + " is ambiguous;\n" + msg); + + var defSym: Symbol = tree.symbol; // the directly found symbol + var pre: Type = NoPrefix; // the prefix type of defSym, if a class member + var qual: Tree = EmptyTree; // the qualififier tree if transformed tree is a select + + if (defSym == NoSymbol) { + var defEntry: ScopeEntry = null; // the scope entry of defSym, if defined in a local scope + + var cx = context; + while (defSym == NoSymbol && cx != NoContext) { + pre = cx.enclClass.prefix; + defEntry = cx.scope.lookupEntry(name); + if (defEntry != null) { + defSym = defEntry.sym; + } else { + cx = cx.enclClass; + defSym = pre.member(name) filter (sym => context.isAccessible(sym, pre, false)); + if (defSym == NoSymbol) cx = cx.outer; + } + } + val symDepth = if (defEntry == null) cx.depth + else cx.depth - (cx.scope.nestingLevel - defEntry.owner.nestingLevel); + var impSym: Symbol = NoSymbol; // the imported symbol + var imports = context.imports; // impSym != NoSymbol => it is imported from imports.head + while (impSym == NoSymbol && !imports.isEmpty && imports.head.depth > symDepth) { + impSym = imports.head.importedSymbol(name); + if (impSym == NoSymbol) imports = imports.tail; + } + + // detect ambiguous definition/import, + // update `defSym' to be the final resolved symbol, + // update `pre' to be `sym's prefix type in case it is an imported member, + // and compute value of: + + // imported symbols take precedence over external package-owned symbols (hack?) + if (defSym.tpe != NoType && impSym.tpe != NoType && defSym.isExternal && defSym.owner.isPackageClass) + defSym = NoSymbol; + + if (defSym.tpe != NoType) { + if (impSym.tpe != NoType) + ambiguousError( + "it is both defined in " + defSym.owner + + " and imported subsequently by \n" + imports.head); + else if (!defSym.owner.isClass || defSym.owner.isPackageClass || defSym.isTypeParameterOrSkolem) + pre = NoPrefix + else + qual = atPos(tree.pos)(gen.mkQualifier(pre)); + } else { + if (impSym.tpe != NoType) { + var impSym1 = NoSymbol; + var imports1 = imports.tail; + def ambiguousImportError = ambiguousError( + "it is imported twice in the same scope by\n" + imports.head + "\nand " + imports1.head); + while (!imports1.isEmpty && imports1.head.depth == imports.head.depth) { + var impSym1 = imports1.head.importedSymbol(name); + if (impSym1 != NoSymbol) { + if (imports1.head.isExplicitImport(name)) { + if (imports.head.isExplicitImport(name)) ambiguousImportError; + impSym = impSym1; + imports = imports1; + } else if (!imports.head.isExplicitImport(name)) ambiguousImportError + } + imports1 = imports1.tail; + } + defSym = impSym; + qual = imports.head.qual; + pre = qual.tpe; + } else { + if (settings.debug.value) { + log(context.imports);//debug + } + error(tree.pos, "not found: " + decode(name)); + defSym = context.owner.newErrorSymbol(name); + } + } + } + if (defSym.owner.isPackageClass) pre = defSym.owner.thisType; + val tree1 = if (qual == EmptyTree) tree + else atPos(tree.pos)(Select(qual, name)); + // atPos necessary because qualifier might come from startContext + //System.out.println("check acc: " + defSym + " " + pre);//DEBUG + stabilize(checkAccessible(tree1, defSym, pre, qual), pre, mode, pt) + } + + // begin typed1 + val sym: Symbol = tree.symbol; + if (sym != null) sym.initialize; + //if (settings.debug.value && tree.isDef) log("typing definition of " + sym);//DEBUG + tree match { + case PackageDef(name, stats) => + val stats1 = newTyper(context.make(tree, sym.moduleClass, sym.info.decls)) + .typedStats(stats, NoSymbol); + copy.PackageDef(tree, name, stats1) setType NoType + + case cdef @ ClassDef(_, _, _, _, _) => + newTyper(context.makeNewScope(tree, sym)).typedClassDef(cdef) + + case mdef @ ModuleDef(_, _, _) => + newTyper(context.make(tree, sym.moduleClass)).typedModuleDef(mdef) + + case vdef @ ValDef(_, _, _, _) => + typedValDef(vdef) + + case ddef @ DefDef(_, _, _, _, _, _) => + newTyper(context.makeNewScope(tree, sym)).typedDefDef(ddef) + + case tdef @ AbsTypeDef(_, _, _, _) => + newTyper(context.makeNewScope(tree, sym)).typedAbsTypeDef(tdef) + + case tdef @ AliasTypeDef(_, _, _, _) => + newTyper(context.makeNewScope(tree, sym)).typedAliasTypeDef(tdef) + + case ldef @ LabelDef(_, _, _) => + var lsym = ldef.symbol; + var typer1 = this; + if (lsym == NoSymbol) { // labeldef is part of template + typer1 = newTyper(context.makeNewScope(tree, context.owner)); + typer1.enterLabelDef(ldef); + } + typer1.typedLabelDef(ldef) + + case Attributed(attr, defn) => + val attr1 = typed(attr, AttributeClass.tpe); + val defn1 = typed(defn, mode, pt); + val ai = attrInfo(attr1); + if (ai != null) defn1.symbol.attributes = defn1.symbol.attributes ::: List(ai); + defn1 + + case DocDef(comment, defn) => + typed(defn, mode, pt); + + case block @ Block(_, _) => + newTyper(context.makeNewScope(tree, context.owner)) + .typedBlock(block, mode, pt) + + case Sequence(elems) => + val elems1 = List.mapConserve(elems)(elem => typed(elem, mode, pt)); + copy.Sequence(tree, elems1) setType pt + + case Alternative(alts) => + val alts1 = List.mapConserve(alts)(alt => typed(alt, mode, pt)); + copy.Alternative(tree, alts1) setType pt + + case Star(elem) => + val elem1 = typed(elem, mode, pt); + copy.Star(tree, elem1) setType pt + + case Bind(name, body) => + var vble = tree.symbol; + if (vble == NoSymbol) vble = context.owner.newValue(tree.pos, name); + if (vble.name != nme.WILDCARD) namer.enterInScope(vble); + val body1 = typed(body, mode, pt); + vble.setInfo(if (treeInfo.isSequenceValued(body)) seqType(body1.tpe) else body1.tpe); + copy.Bind(tree, name, body1) setSymbol vble setType body1.tpe; // buraq, was: pt + + case ArrayValue(elemtpt, elems) => + val elemtpt1 = typedType(elemtpt); + val elems1 = List.mapConserve(elems)(elem => typed(elem, mode, elemtpt1.tpe)); + copy.ArrayValue(tree, elemtpt1, elems1) + .setType(if (isFullyDefined(pt) && !phase.erasedTypes) pt + else appliedType(ArrayClass.typeConstructor, List(elemtpt1.tpe))) + + case fun @ Function(_, _) => +/* + newTyper(context.makeNewScope(tree, context.owner)).typedFunction(fun, mode, pt) +*/ + tree.symbol = context.owner.newValue(tree.pos, nme.ANON_FUN_NAME) + .setFlag(SYNTHETIC).setInfo(NoType); + newTyper(context.makeNewScope(tree, tree.symbol)).typedFunction(fun, mode, pt) + + case Assign(lhs, rhs) => + def isGetter(sym: Symbol) = sym.info match { + case PolyType(List(), _) => sym.owner.isClass && !sym.isStable + case _ => false + } + val lhs1 = typed(lhs); + val varsym = lhs1.symbol; + if (varsym != null && isGetter(varsym)) { + lhs1 match { + case Select(qual, name) => + typed( + Apply( + Select(qual, nme.getterToSetter(name)) setPos lhs.pos, + List(rhs)) setPos tree.pos, mode, pt) + } + } else if (varsym != null && (varsym.isVariable || varsym.isValue && phase.erasedTypes)) { + val rhs1 = typed(rhs, lhs1.tpe); + copy.Assign(tree, lhs1, rhs1) setType UnitClass.tpe; + } else { + System.out.println("" + lhs1 + " " + varsym + " " + varsym.isValue + " " + flagsToString(varsym.flags));//debug + if (!lhs1.tpe.isError) error(tree.pos, "assignment to non-variable "); + setError(tree) + } + + case If(cond, thenp, elsep) => + val cond1 = typed(cond, BooleanClass.tpe); + if (elsep.isEmpty) { + val thenp1 = typed(thenp, UnitClass.tpe); + copy.If(tree, cond1, thenp1, elsep) setType UnitClass.tpe + } else { + val thenp1 = typed(thenp, pt); + val elsep1 = typed(elsep, pt); + copy.If(tree, cond1, thenp1, elsep1) setType ptOrLub(List(thenp1.tpe, elsep1.tpe)); + } + + case Match(selector, cases) => + val selector1 = typed(selector); + val cases1 = typedCases(tree, cases, selector1.tpe.widen, pt); + copy.Match(tree, selector1, cases1) setType ptOrLub(cases1 map (.tpe)) + + case Return(expr) => + val enclFun = if (tree.symbol != NoSymbol) tree.symbol else context.owner.enclMethod; + if (!enclFun.isMethod || enclFun.isConstructor) + errorTree(tree, "return outside method definition") + else if (!context.owner.isInitialized) + errorTree(tree, "method " + context.owner + " has return statement; needs result type") + else { + val expr1: Tree = typed(expr, enclFun.tpe.finalResultType); + copy.Return(tree, expr1) setSymbol enclFun setType AllClass.tpe; + } + + case Try(block, catches, finalizer) => + val block1 = typed(block, pt); + val catches1 = typedCases(tree, catches, ThrowableClass.tpe, pt); + val finalizer1 = if (finalizer.isEmpty) finalizer + else typed(finalizer, UnitClass.tpe); + copy.Try(tree, block1, catches1, finalizer1) + .setType(ptOrLub(block1.tpe :: (catches1 map (.tpe)))) + + case Throw(expr) => + val expr1 = typed(expr, ThrowableClass.tpe); + copy.Throw(tree, expr1) setType AllClass.tpe + + case New(tpt: Tree) => + var tpt1 = typedTypeConstructor(tpt); + if (tpt1.hasSymbol && !tpt1.symbol.typeParams.isEmpty) { + context.undetparams = cloneSymbols(tpt1.symbol.unsafeTypeParams); + tpt1 = TypeTree() + .setPos(tpt1.pos) + .setType(appliedType(tpt1.tpe, context.undetparams map (.tpe))); + } + if (tpt1.tpe.symbol.isTrait) error(tree.pos, "traits cannot be instantiated"); + copy.New(tree, tpt1).setType(tpt1.tpe) + + case Typed(expr, tpt @ Ident(name)) if (name == nme.WILDCARD_STAR.toTypeName) => + val expr1 = typed(expr, mode & stickyModes, seqType(pt)); + expr1.tpe.baseType(SeqClass) match { + case TypeRef(_, _, List(elemtp)) => + copy.Typed(tree, expr1, tpt setType elemtp) setType elemtp + case _ => + setError(tree) + } + case Typed(expr, tpt) => + val tpt1 = typedType(tpt); + val expr1 = typed(expr, mode & stickyModes, tpt1.tpe); + copy.Typed(tree, expr1, tpt1) setType tpt1.tpe + + case TypeApply(fun, args) => + val args1 = List.mapConserve(args)(typedType); + // do args first in order to maintain conext.undetparams on the function side. + typedTypeApply(typed(fun, funmode | TAPPmode, WildcardType), args1) + + case Apply(Block(stats, expr), args) => + typed1(Block(stats, Apply(expr, args)), mode, pt) + + case Apply(fun, args) => + val stableApplication = fun.symbol != null && fun.symbol.isMethod && fun.symbol.isStable; + if (stableApplication && (mode & PATTERNmode) != 0) { + // treat stable function applications f() as expressions. + typed1(tree, mode & ~PATTERNmode | EXPRmode, pt) + } else { + val funpt = if ((mode & PATTERNmode) != 0) pt else WildcardType; + var fun1 = typed(fun, funmode, funpt); + if (stableApplication) fun1 = stabilizeFun(fun1, mode, pt); + // if function is overloaded, filter all alternatives that match + // number of arguments and expected result type. + // if (settings.debug.value) log("trans app " + fun1 + ":" + fun1.symbol + ":" + fun1.tpe + " " + args);//DEBUG + if (fun1.hasSymbol && fun1.symbol.hasFlag(OVERLOADED)) { + val argtypes = args map (arg => AllClass.tpe); + val pre = fun1.symbol.tpe.prefix; + val sym = fun1.symbol filter (alt => + isApplicable(context.undetparams, pre.memberType(alt), argtypes, pt)); + if (sym != NoSymbol) + fun1 = adapt(fun1 setSymbol sym setType pre.memberType(sym), funmode, WildcardType) + } + if (util.Statistics.enabled) appcnt = appcnt + 1; + typedApply(fun1, args) + } + + case Super(qual, mix) => + val Pair(clazz, selftype) = + if (tree.symbol != NoSymbol) { + Pair(tree.symbol, tree.symbol.thisType) + } else { + val clazzContext = qualifyingClassContext(qual); + Pair(clazzContext.owner, clazzContext.prefix) + } + if (clazz == NoSymbol) setError(tree) + else { + val owntype = + if (mix == nme.EMPTY.toTypeName) + if ((mode & SUPERCONSTRmode) != 0) clazz.info.parents.head + else intersectionType(clazz.info.parents) + else { + val ps = clazz.info.parents dropWhile (p => p.symbol.name != mix); + if (ps.isEmpty) { + System.out.println(clazz.info.parents map (.symbol.name));//debug + error(tree.pos, "" + mix + " does not name a base class of " + clazz); + ErrorType + } else ps.head + } + tree setSymbol clazz setType SuperType(selftype, owntype) + } + + case This(qual) => + val Pair(clazz, selftype) = + if (tree.symbol != NoSymbol) { + Pair(tree.symbol, tree.symbol.thisType) + } else { + val clazzContext = qualifyingClassContext(qual); + Pair(clazzContext.owner, clazzContext.prefix) + } + if (clazz == NoSymbol) setError(tree) + else { + val owntype = if (pt.isStable || (mode & QUALmode) != 0) selftype + else selftype.singleDeref; + tree setSymbol clazz setType owntype + } + + case Select(qual @ Super(_, _), nme.CONSTRUCTOR) => + val qual1 = typed(qual, EXPRmode | QUALmode | POLYmode | SUPERCONSTRmode, WildcardType); + // the qualifier type of a supercall constructor is its first parent class + typedSelect(qual1, nme.CONSTRUCTOR); + + case Select(qual, name) => + if (util.Statistics.enabled) selcnt = selcnt + 1; + var qual1 = typedQualifier(qual); + if (name.isTypeName) qual1 = checkStable(qual1); + typedSelect(qual1, name); + + case Ident(name) => + idcnt = idcnt + 1; + if (name == nme.WILDCARD && (mode & (PATTERNmode | FUNmode)) == PATTERNmode) + tree setType pt + else + typedIdent(name) + + // todo: try with case Literal(Constant(())) + case Literal(value) => + tree setType ( + if (value.tag == UnitTag) UnitClass.tpe + else ConstantType(value)) + + case SingletonTypeTree(ref) => + val ref1 = checkStable(typed(ref, EXPRmode | QUALmode, AnyRefClass.tpe)); + tree setType ref1.tpe.resultType; + + case SelectFromTypeTree(qual, selector) => + tree setType typedSelect(typedType(qual), selector).tpe + + case CompoundTypeTree(templ: Template) => + tree setType { + val parents1 = List.mapConserve(templ.parents)(typedType); + if (parents1 exists (.tpe.isError)) ErrorType + else { + val decls = new Scope(); + val self = refinedType(parents1 map (.tpe), context.enclClass.owner, decls); + newTyper(context.make(templ, self.symbol, decls)).typedRefinement(templ.body); + self + } + } + + case AppliedTypeTree(tpt, args) => + val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType); + val tparams = tpt1.symbol.typeParams; + val args1 = List.mapConserve(args)(typedType); + if (tpt1.tpe.isError) { + setError(tree) + } else if (tparams.length == args1.length) { + val argtypes = args1 map (.tpe); + val owntype = if (tpt1.symbol.isClass) appliedType(tpt1.tpe, argtypes) + else tpt1.tpe.subst(tparams, argtypes); + TypeTree(owntype) setOriginal(tree) // setPos tree.pos + } else if (tparams.length == 0) { + errorTree(tree, "" + tpt1.tpe + " does not take type parameters") + } else { + //System.out.println("\{tpt1}:\{tpt1.symbol}:\{tpt1.symbol.info}"); + System.out.println("" + tpt1 + ":" + tpt1.symbol + ":" + tpt1.symbol.info);//debug + errorTree(tree, "wrong number of type arguments for " + tpt1.tpe + ", should be " + tparams.length) + } + case _ => + throw new Error("unexpected tree: " + tree);//debug + } + } + + def typed(tree: Tree, mode: int, pt: Type): Tree = + try { + if (settings.debug.value) { + assert(pt != null, tree);//debug + //System.out.println("typing " + tree);//DEBUG + } + val tree1 = if (tree.tpe != null) tree else typed1(tree, mode, pt); + //System.out.println("typed " + tree1 + ":" + tree1.tpe);//debug + val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt); + //System.out.println("adapted " + tree1 + ":" + tree1.tpe + " to " + pt);//debug + result + } catch { + case ex: TypeError => + //System.out.println("caught " + ex + " in typed");//DEBUG + reportTypeError(tree.pos, ex); + setError(tree) + case ex: Throwable => + if (settings.debug.value) + System.out.println("exception when typing " + tree + ", pt = " + pt); + throw(ex) + } + + def atOwner(owner: Symbol): Typer = + new Typer(context.make(context.tree, owner)); + + def atOwner(tree: Tree, owner: Symbol): Typer = + new Typer(context.make(tree, owner)); + + /** Types expression or definition `tree' */ + def typed(tree: Tree): Tree = + typed(tree, EXPRmode, WildcardType); + + /** Types expression `tree' with given prototype `pt' */ + def typed(tree: Tree, pt: Type): Tree = + typed(tree, EXPRmode, pt); + + /** Types qualifier `tree' of a select node. E.g. is tree occurs in acontext like `tree.m'. */ + def typedQualifier(tree: Tree): Tree = + typed(tree, EXPRmode | QUALmode | POLYmode, WildcardType); + + /** Types function part of an application */ + def typedOperator(tree: Tree): Tree = + typed(tree, EXPRmode | FUNmode | POLYmode | TAPPmode, WildcardType); + + /** Types a pattern with prototype `pt' */ + def typedPattern(tree: Tree, pt: Type): Tree = + typed(tree, PATTERNmode, pt); + + /** Types a (fully parameterized) type tree */ + def typedType(tree: Tree): Tree = + typed(tree, TYPEmode, WildcardType); + + /** Types a type constructor tree used in a new or supertype */ + def typedTypeConstructor(tree: Tree): Tree = { + val result = typed(tree, TYPEmode | FUNmode, WildcardType); + if (!phase.erasedTypes && result.tpe.isInstanceOf[TypeRef] && !result.tpe.prefix.isStable) + error(tree.pos, result.tpe.prefix.toString() + " is not a legal prefix for a constructor"); + result + } + + def computeType(tree: Tree): Type = { + val tree1 = typed(tree); + transformed(tree) = tree1; + tree1.tpe + } + + def transformedOrTyped(tree: Tree, pt: Type): Tree = transformed.get(tree) match { + case Some(tree1) => transformed -= tree; tree1 + case None => typed(tree, pt) + } + +/* + def convertToTypeTree(tree: Tree): Tree = tree match { + case TypeTree() => tree + case _ => TypeTree(tree.tpe) + } +*/ + /* -- Views --------------------------------------------------------------- */ + + private def depoly(tp: Type): Type = tp match { + case PolyType(tparams, restpe) => restpe.subst(tparams, tparams map (t => WildcardType)) + case _ => tp + } + + private def typedImplicit(pos: int, info: ImplicitInfo, pt: Type, local: boolean): Tree = + if (isCompatible(depoly(info.tpe), pt)) { + var tree: Tree = EmptyTree; + def fail(reason: String): Tree = { + if (settings.debug.value) + log(tree.toString() + " is not a valid implicit value because:\n" + reason); + EmptyTree + } + try { + tree = Ident(info.name) setPos pos; + if (!local) tree setSymbol info.sym; + tree = typed1(tree, EXPRmode, pt); + if (settings.debug.value) + log("typed implicit " + tree + ":" + tree.tpe + ", pt = " + pt);//debug + val tree1 = adapt(tree, EXPRmode, pt); + if (settings.debug.value) + log("adapted implicit " + tree.symbol + ":" + tree1.tpe + " to " + pt);//debug + if (info.sym == tree.symbol) tree1 + else fail("syms differ: " + tree.symbol + " " + info.sym) + } catch { + case ex: TypeError => fail(ex.getMessage()) + } + } else EmptyTree; + + private def inferImplicit(pos: int, pt: Type, isView: boolean, reportAmbiguous: boolean): Tree = { + + if (util.Statistics.enabled) implcnt = implcnt + 1; + val startTime = if (util.Statistics.enabled) System.currentTimeMillis() else 0l; + + def isBetter(sym1: Symbol, tpe1: Type, sym2: Symbol, tpe2: Type): boolean = ( + sym2.isError || + (sym1.owner != sym2.owner) && (sym1.owner isSubClass sym2.owner) && (tpe1 matches tpe2) + ); + val tc = newTyper(context.makeImplicit(reportAmbiguous)); + + def searchImplicit(implicitInfoss: List[List[ImplicitInfo]], local: boolean): Tree = { + var iss = implicitInfoss; + var tree: Tree = EmptyTree; + while (tree == EmptyTree && !iss.isEmpty) { + var is = iss.head; + iss = iss.tail; + while (!is.isEmpty) { + tree = tc.typedImplicit(pos, is.head, pt, local); + if (settings.debug.value) log("tested " + is.head.sym + is.head.sym.locationString + ":" + is.head.tpe + "=" + tree);//debug + val is0 = is; + is = is.tail; + if (tree != EmptyTree) { + while (!is.isEmpty) { + val tree1 = tc.typedImplicit(pos, is.head, pt, local); + if (tree1 != EmptyTree) { + if (isBetter(is.head.sym, tree1.tpe, is0.head.sym, tree.tpe)) + tree = tree1 + else if (!isBetter(is0.head.sym, tree.tpe, is.head.sym, tree1.tpe)) + error( + pos, + "ambiguous implicit value:\n" + + " both " + is0.head.sym + is0.head.sym.locationString + " of type " + tree.tpe + + "\n and " + is.head.sym + is.head.sym.locationString + " of type " + tree1.tpe + + (if (isView) + "\n are possible conversion functions from " + + pt.typeArgs(0) + " to " + pt.typeArgs(1) + else + "\n match expected type " + pt)); + } + is = is.tail + } + } + } + } + tree + } + + def implicitsOfType(tp: Type): List[List[ImplicitInfo]] = { + val tp1 = if (isFunctionType(tp)) intersectionType(tp.typeArgs.reverse) else tp; + tp1.baseClasses map implicitsOfClass; + } + + def implicitsOfClass(clazz: Symbol): List[ImplicitInfo] = ( + clazz.initialize.linkedModule.moduleClass.info.decls.toList.filter(.hasFlag(IMPLICIT)) map + (sym => ImplicitInfo(sym.name, clazz.linkedModule.tpe.memberType(sym), sym)) + ); + + var tree = searchImplicit(context.implicitss, true); + if (tree == EmptyTree) tree = searchImplicit(implicitsOfType(pt.widen), false); + if (util.Statistics.enabled) impltime = impltime + System.currentTimeMillis() - startTime; + tree + } + + def applyImplicitArgs(tree: Tree): Tree = tree.tpe match { + case MethodType(formals, _) => + def implicitArg(pt: Type) = { + val arg = inferImplicit(tree.pos, pt, false, true); + if (arg != EmptyTree) arg + else errorTree(tree, "no implicit argument matching parameter type " + pt + " was found.") + } + Apply(tree, formals map implicitArg) setPos tree.pos + } + } +} + diff --git a/src/compiler/scala/tools/nsc/typechecker/Variances.scala b/src/compiler/scala/tools/nsc/typechecker/Variances.scala new file mode 100644 index 0000000000..ca7c52d242 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Variances.scala @@ -0,0 +1,83 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.typechecker; + +import symtab.Flags._; + +/** Variances form a lattice, 0 <= COVARIANT <= Variances, 0 <= CONTRAVARIANT <= VARIANCES + */ +[_trait_] abstract class Variances { + + val global: Global; + import global._; + + /** Convert variance to string */ + private def varianceString(variance: int): String = + if (variance == COVARIANT) "covariant" + else if (variance == CONTRAVARIANT) "contravariant" + else "invariant"; + + /** Flip between covariant and contravariant */ + private def flip(v: int): int = { + if (v == COVARIANT) CONTRAVARIANT; + else if (v == CONTRAVARIANT) COVARIANT; + else v + } + + private def compose(v1: int, v2: int) = + if (v1 == 0) 0 + else if (v1 == CONTRAVARIANT) flip(v2) + else v2; + + /** Map everything below VARIANCES to 0 */ + private def cut(v: int): int = + if (v == VARIANCES) v else 0; + + /** Compute variance of type parameter `tparam' in types of all symbols `sym'. */ + def varianceInSyms(syms: List[Symbol])(tparam: Symbol): int = + (VARIANCES /: syms) ((v, sym) => v & varianceInSym(sym)(tparam)); + + /** Compute variance of type parameter `tparam' in type of symbol `sym'. */ + def varianceInSym(sym: Symbol)(tparam: Symbol): int = + 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): int = + (VARIANCES /: tps) ((v, tp) => v & varianceInType(tp)(tparam)); + + /** Compute variance of type parameter `tparam' in all type arguments + * `tps' which correspond to formal type parameters `tparams1'. */ + def varianceInArgs(tps: List[Type], tparams1: List[Symbol])(tparam: Symbol): int = { + var v: int = VARIANCES; + for (val Pair(tp, tparam1) <- tps zip tparams1) { + val v1 = varianceInType(tp)(tparam); + v = v & (if (tparam1.hasFlag(COVARIANT)) v1 + else if (tparam1.hasFlag(CONTRAVARIANT)) flip(v1) + else cut(v1)) + } + v + } + + /** Compute variance of type parameter `tparam' in type `tp'. */ + def varianceInType(tp: Type)(tparam: Symbol): int = tp match { + case ErrorType | WildcardType | NoType | NoPrefix | ThisType(_) | ConstantType(_) => + VARIANCES + case SingleType(pre, sym) => + cut(varianceInType(pre)(tparam)) + case TypeRef(pre, sym, args) => + if (sym == tparam) COVARIANT + else varianceInType(pre)(tparam) & varianceInArgs(args, sym.typeParams)(tparam) + case TypeBounds(lo, hi) => + flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam) + case RefinedType(parents, defs) => + varianceInTypes(parents)(tparam) & varianceInSyms(defs.toList)(tparam) + case MethodType(formals, restpe) => + flip(varianceInTypes(formals)(tparam)) & varianceInType(restpe)(tparam) + case PolyType(tparams, restpe) => + flip(varianceInSyms(tparams)(tparam)) & varianceInType(restpe)(tparam) + } +} diff --git a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala new file mode 100644 index 0000000000..182a730694 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala @@ -0,0 +1,99 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.util; + +import scala.tools.nsc.util.SourceFile.{LF, FF, CR, SU} + +class CharArrayReader(buf: Array[char], start: int, /* startline: int, startcol: int, */ + decodeUni: boolean, error: String => unit) { + + def this(buf: Array[char], decodeUni: boolean, error: String => unit) = + this(buf, 0, /* 1, 1, */ decodeUni, error); + + /** layout constant + */ + val tabinc = 8; + + /** the line and column position of the current character + */ + var ch: char = _; + var bp = start; + //private var cline: int = _; + //private var ccol: int = _; + def cpos = bp; + var isUnicode: boolean = _; + var lastLineStartPos: int = 0; + var lineStartPos: int = 0; + //private var nextline = startline; + //private var nextcol = startcol; + + def hasNext: boolean = bp < buf.length; + + def last: char = if(bp > start + 2) buf(bp - 2) else ' '; // XML literals + + def next: unit = { + //cline = nextline; + //ccol = nextcol; + ch = buf(bp); + isUnicode = false; + bp = bp + 1; + ch match { + case '\t' => + // nextcol = ((nextcol - 1) / tabinc * tabinc) + tabinc + 1; + case CR => + //nextline = nextline + 1; + // nextcol = 1; + if (buf(bp) == LF) { + ch = LF; + bp = bp + 1 + } + lastLineStartPos = lineStartPos; + lineStartPos = bp; + case LF | FF => + lastLineStartPos = lineStartPos; + lineStartPos = bp; + //nextline = nextline + 1; + //nextcol = 1 + case '\\' => + def evenSlashPrefix: boolean = { + var p = bp - 2; + while (p >= 0 && buf(p) == '\\') p = p - 1; + (bp - p) % 2 == 0 + } + def udigit: int = { + val d = digit2int(buf(bp), 16); + if (d >= 0) { bp = bp + 1; /* nextcol = nextcol + 1 */ } + else error("error in unicode escape"); + d + } + // nextcol = nextcol + 1; + if (buf(bp) == 'u' && decodeUni && evenSlashPrefix) { + do { + bp = bp + 1; // nextcol = nextcol + 1; + } while (buf(bp) == 'u'); + val code = udigit << 12 | udigit << 8 | udigit << 4 | udigit; + ch = code.asInstanceOf[char]; + isUnicode = true + } + case _ => + // nextcol = nextcol + 1 + } + } + + def copy: CharArrayReader = + new CharArrayReader(buf, bp, /* nextcol, nextline, */ decodeUni, error); + + def digit2int(ch: char, base: int): int = { + if ('0' <= ch && ch <= '9' && ch < '0' + base) + ch - '0' + else if ('A' <= ch && ch < 'A' + base - 10) + ch - 'A' + 10 + else if ('a' <= ch && ch < 'a' + base - 10) + ch - 'a' + 10 + else + -1 + } +} diff --git a/src/compiler/scala/tools/nsc/util/FreshNameCreator.scala b/src/compiler/scala/tools/nsc/util/FreshNameCreator.scala new file mode 100644 index 0000000000..8215941256 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/FreshNameCreator.scala @@ -0,0 +1,33 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.util; + +import scala.collection.mutable.HashMap; + +class FreshNameCreator { + + protected var counter = 0; + protected val counters = new HashMap[String,int]; + + /** + * 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 count = counters.get(prefix) match { + case Some(last) => last + 1 + case None => 0 + } + counters.update(prefix, count); + prefix + count + } + + def newName(): String = { + counter = counter + 1; + "$" + counter + "$" + } +} diff --git a/src/compiler/scala/tools/nsc/util/HashSet.scala b/src/compiler/scala/tools/nsc/util/HashSet.scala new file mode 100644 index 0000000000..d74d7e9582 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/HashSet.scala @@ -0,0 +1,58 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.util; + +class HashSet[T <: AnyRef](initialCapacity: int) extends Set[T] { + + private var capacity = initialCapacity; + private var used = 0; + private var table = new Array[Object](capacity); + + def size: int = used; + + def findEntry(x: T): T = { + var h = x.hashCode() % capacity; + var entry = table(h); + while (entry != null && entry != x) { + h = (h + 1) % capacity; + entry = table(h) + } + entry.asInstanceOf[T] + } + + def addEntry(x: T): unit = { + if (used >= (capacity >> 2)) growTable; + used = used + 1; + var h = x.hashCode() % capacity; + while (table(h) != null) { + h = (h + 1) % capacity + } + table(h) = x + } + + def elements = new Iterator[T] { + private var i = 0; + def hasNext: boolean = { + while (i < capacity && table(i) == null) i = i + 1; + i < capacity + } + def next: T = + if (hasNext) { i = i + 1; table(i - 1).asInstanceOf[T] } + else null + } + + private def growTable: unit = { + val oldtable = table; + capacity = capacity * 2; + table = new Array[Object](capacity); + var i = 0; + while (i < oldtable.length) { + val entry = oldtable(i); + if (entry != null) addEntry(entry.asInstanceOf[T]); + i = i + 1 + } + } +} diff --git a/src/compiler/scala/tools/nsc/util/LinkedList.scala b/src/compiler/scala/tools/nsc/util/LinkedList.scala new file mode 100644 index 0000000000..375e6ca615 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/LinkedList.scala @@ -0,0 +1,11 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.util; + +class LinkedList[T] { + var next: LinkedList[T] = null; + var elem: T = _; +} diff --git a/src/compiler/scala/tools/nsc/util/ListBuffer.scala b/src/compiler/scala/tools/nsc/util/ListBuffer.scala new file mode 100644 index 0000000000..bdc09412eb --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/ListBuffer.scala @@ -0,0 +1,58 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.util; + +class ListBuffer[T] extends Iterator[T] { + + private var first = new LinkedList[T]; + private var limit = first; + + def +=(x: T): unit = { + limit.elem = x; + limit.next = new LinkedList[T]; + limit = limit.next; + } + + def ++=(xs: Iterable[T]): unit = + for (val x <- xs.elements) +=(x); + + def +(x: T): ListBuffer[T] = { +=(x); this } + def ++(xs: Iterable[T]): ListBuffer[T] = { ++=(xs); this } + + def hasNext: boolean = + first != limit; + + def next: T = { + assert(hasNext); + val x = first.elem; + first = first.next; + x + } + + def elements: Iterator[T] = new Iterator[T] { + var first = ListBuffer.this.first; + + def hasNext: boolean = + first != limit; + + def next: T = { + assert(hasNext); + val x = first.elem; + first = first.next; + x + } + } + + def clear: unit = { first = limit } + + /** override for efficiency */ + override def toList: List[T] = { + def mkList(p: LinkedList[T]): List[T] = if (p == limit) List() else p.elem :: mkList(p.next); + mkList(first) + } + + override def toString(): String = toList.mkString("", ",", ""); +} diff --git a/src/compiler/scala/tools/nsc/util/NameTransformer.scala b/src/compiler/scala/tools/nsc/util/NameTransformer.scala new file mode 100644 index 0000000000..709bd00a84 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/NameTransformer.scala @@ -0,0 +1,96 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.util; + +object NameTransformer { + 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)) + } + + 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"); + + /** Replace operator symbols by corresponding "$op_name" */ + def encode(name: String): String = { + var buf: StringBuffer = null; + val len = name.length(); + var i = 0; + while (i < len) { + val c = name charAt i; + if (c < nops && op2code(c) != null) { + if (buf == null) { + buf = new StringBuffer(); + buf.append(name.substring(0, i)); + } + buf.append(op2code(c)); + } else if (buf != null) { + buf.append(c) + } + i = i + 1 + } + if (buf == null) name else buf.toString() + } + + /** Replace $op_name by corresponding operator symbol */ + def decode(name: String): String = { + //System.out.println("decode: " + name);//DEBUG + var buf: StringBuffer = null; + val len = name.length(); + var i = 0; + while (i < len) { + var ops: OpCodes = null; + 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 != null && !name.startsWith(ops.code, i)) ops = ops.next; + if (ops != null) { + if (buf == null) { + buf = new StringBuffer(); + buf.append(name.substring(0, i)); + } + buf.append(ops.op); + i = i + ops.code.length() + } + } + } + } + if (ops == null) { + if (buf != null) buf.append(c); + i = i + 1 + } + } + //System.out.println("= " + (if (buf == null) name else buf.toString()));//DEBUG + if (buf == null) name else buf.toString() + } +} diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala new file mode 100644 index 0000000000..bf502cac3b --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -0,0 +1,79 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.tools.nsc.util; + +/** This position uses offset in character buffer rather than line column relationship. + * @author Sean McDirmid + */ +object Position { + val NOPOS = -1; + val FIRSTPOS = 0; + val NOLINE = 0; + val FIRSTLINE = 1; + + def line(source : SourceFile, offset : Int) = (new Position(source, offset)).line; +} + + +class Position( val source : SourceFile, val offset: Int) { + import Position._; + + private val tabInc = 8; + + def this(sourceName : String) = this(new SourceFile(sourceName, new Array[Char](0)), Position.NOPOS); + def this(sourceName : String, _offset : Int) = this(new SourceFile(sourceName, new Array[Char](0)), _offset); + + def hasOffset = offset != NOPOS; + + def line: Int = if (hasOffset) source.offsetToLine(offset) + FIRSTLINE else NOLINE; + // for display purposes only. + def column: Int = if (hasOffset) { + var column = 1; + + // find beginning offset for line + val line = source.offsetToLine (offset); + var coffset = source. lineToOffset(line); + var continue = true; + while (continue) { + if (coffset == offset) continue = false; + else if (source.content(coffset) == '\t') column = ((column - 1) / tabInc * tabInc) + tabInc + 1; + else column = column + 1; + coffset = coffset + 1; + } + column; + } else 0; + + + def dbgString = + if (!hasOffset) "NOP" + else if (offset >= source.content.length) "OB-" + offset else { + val ret = "offset=" + offset + " line=" + line; + var add = ""; + while (offset + add.length() < source.content.length && + add.length() < 10) add = add + source.content(offset + add.length()); + ret + " c[0..9]=\"" + add + "\""; + } + + + + def lineContent: String = if (hasOffset) source.lineToString(line - FIRSTLINE) else "NO_LINE"; + + /** Returns a string representation of the encoded position. */ + override def toString(): String = { + val sb = new StringBuffer(); + sb.append(source.file.getPath()); + if (hasOffset) { + sb.append(line); + sb.append(':'); + sb.append(column); + } + sb.toString(); + } +} diff --git a/src/compiler/scala/tools/nsc/util/Set.scala b/src/compiler/scala/tools/nsc/util/Set.scala new file mode 100644 index 0000000000..dab7898e39 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/Set.scala @@ -0,0 +1,23 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.util; + +/** A common trait for lightweight sets. + */ +abstract class Set[T <: AnyRef] { + + def findEntry(x: T): T; + + def addEntry(x: T): unit; + + def elements: Iterator[T]; + + def contains(x: T): boolean = + findEntry(x) != null; + + def toList = elements.toList; + +} diff --git a/src/compiler/scala/tools/nsc/util/ShowPickled.scala b/src/compiler/scala/tools/nsc/util/ShowPickled.scala new file mode 100644 index 0000000000..481b7278e4 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/ShowPickled.scala @@ -0,0 +1,161 @@ +package scala.tools.nsc.util; + +import symtab.Names; +import symtab.classfile.{PickleBuffer, PickleFormat}; +import symtab.Flags; +import java.io._; +import java.lang.{Integer, Float, Double} + +object ShowPickled extends Names { + + import PickleFormat._; + + 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 CLASSINFOtpe => "CLASSINFOtpe"; + case METHODtpe => "METHODtpe"; + case POLYtpe => "POLYtpe"; + 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 LITERALzero => "LITERALzero"; + case _ => "***BAD TAG***(" + tag + ")"; + } + + def printFile(buf: PickleBuffer, out: PrintStream): unit = { + val index = buf.createIndex; + + def printNameRef() = { + val x = buf.readNat(); + val savedIndex = buf.readIndex; + buf.readIndex = index(x); + val tag = buf.readByte(); + val len = buf.readNat(); + out.print(" " + x + "(" + newTermName(buf.bytes, buf.readIndex, len) + ")"); + buf.readIndex = savedIndex + } + + def printNat() = out.print(" " + buf.readNat()); + def printSymbolRef() = printNat(); + def printTypeRef() = printNat(); + def printConstantRef() = printNat(); + + def printSymInfo() = { + printNameRef(); + printSymbolRef(); + val flags = buf.readNat(); + out.print(" " + Integer.toHexString(flags) + "[" + Flags.flagsToString(flags) + "] "); + printTypeRef(); + } + + def printEntry(i: int): unit = { + buf.readIndex = index(i); + out.print(i + "," + buf.readIndex + ": "); + val tag = buf.readByte(); + out.print(tag2string(tag)); + val len = buf.readNat(); + val end = len + buf.readIndex; + out.print(" " + len + ":"); + tag match { + case TERMname => + out.print(" "); + out.print(newTermName(buf.bytes, buf.readIndex, len).toString()); + buf.readIndex = end; + case TYPEname => + out.print(" "); + out.print(newTypeName(buf.bytes, buf.readIndex, len)); + buf.readIndex = end; + case TYPEsym | ALIASsym | CLASSsym | MODULEsym | VALsym => + printSymInfo(); + 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 => + printTypeRef(); buf.until(end, printTypeRef) + case POLYtpe => + printTypeRef(); buf.until(end, printSymbolRef) + case LITERALboolean => + out.print(if (buf.readLong(len) == 0) " false" else " true") + case LITERALbyte => + out.print(" " + buf.readLong(len).asInstanceOf[byte]) + case LITERALshort => + out.print(" " + buf.readLong(len).asInstanceOf[short]) + case LITERALchar => + out.print(" " + buf.readLong(len).asInstanceOf[char]) + case LITERALint => + out.print(" " + buf.readLong(len).asInstanceOf[int]) + case LITERALlong => + out.print(" " + buf.readLong(len)) + case LITERALfloat => + out.print(" " + Float.intBitsToFloat(buf.readLong(len).asInstanceOf[int])) + case LITERALdouble => + out.print(" " + Double.longBitsToDouble(buf.readLong(len))) + case LITERALstring => + printNameRef(); + case LITERALnull => + out.print(" <null>") + case _ => + } + out.println(); + if (buf.readIndex != end) + out.println("BAD ENTRY END: , computed = " + end + ", factual = " + buf.readIndex); + } + + for (val i <- Iterator.range(0, index.length)) + printEntry(i); + } + + def main(args: Array[String]): unit = { + val file = new File(args(0)); + try { + val stream = new FileInputStream(file); + val data = new Array[byte](stream.available()); + stream.read(data); + val pickle = new PickleBuffer(data, 0, data.length); + printFile(pickle, System.out); + } catch { + case ex: IOException => System.out.println("cannot read " + file + ": " + ex.getMessage()); + } + } +} diff --git a/src/compiler/scala/tools/nsc/util/SourceFile.scala b/src/compiler/scala/tools/nsc/util/SourceFile.scala new file mode 100644 index 0000000000..0d4354d325 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/SourceFile.scala @@ -0,0 +1,136 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.tools.nsc.util; +import scala.tools.util.AbstractFile; +import scala.tools.util.CharArrayFile; + +/** Uses positions that are offsets rather than line/column pairs. + * + * @author Sean McDirmid + */ +object SourceFile { + val LF : Char = 0x0A; + val FF : Char = 0x0C; + val CR : Char = 0x0D; + val SU : Char = 0x1A; + def isLineBreak(c : Char) = c == LF || c == FF || c == CR || c == SU; +} + + +class SourceFile(_file : AbstractFile, _content : Array[Char]) { + import SourceFile._; + + + val file = _file; + val content = normalize(_content); + + def getContent() = content; + + def getFile() = file; + + def this(sourceName: String, content : Array[Char]) = + this(new CharArrayFile(sourceName, content), content); + + def isLineBreak(idx : Int) = if (!SourceFile.isLineBreak(content(idx))) false; + else if (content(idx) == CR && idx + 1 < content.length && content(idx + 1) == LF) false; + else true; + + + def position(offset : Int) = new Position(this, offset); + def position(line : Int, column : Int) = new Position(this, lineToOffset(line) + column); + + // constants + + // NOTE: all indexes are based on zero!!!! + override def toString(): String = file.getName() /* + ":" + content.length */ ; + + + def dbg(offset : Int) = (new Position(this, offset)).dbgString; + + + object line { + var index = 0; + var offset = 0; + + def find(toFind : Int, isIndex : Boolean) : Int = { + if (toFind == 0) return 0; + + if (!isIndex) assert(toFind != Position.NOPOS); + if ( isIndex) assert(toFind > Position.NOLINE - Position.FIRSTLINE); + + if (!isIndex && (toFind >= content.length)) throw new Error(toFind + " not valid offset in " + file.getName() + ":" + content.length); + + def get(isIndex : Boolean) = if (isIndex) index else offset; + + val isBackward = toFind <= get(isIndex); + val increment = if (isBackward) -1 else + 1; + val oneIfBackward = if (isBackward) +1 else 0; + + // System.err.println("FIND-0: " + toFind + " " + isIndex); + + while (true) { + // System.err.println("FIND-1: " + offset + " " + index); + + if (!isIndex && offset == toFind) return index; + if ( isBackward && offset <= 0) throw new Error(offset + " " + index + " " + toFind + " " + isIndex); + offset = offset + increment; + if (!isBackward) assert(offset < content.length); + + if (isLineBreak(offset + (if (isBackward) 0 else -1))) { + index = index + increment; + if (isIndex && index + oneIfBackward == toFind) + return offset + oneIfBackward; + } + } + throw new Error(); + } + } + def offsetToLine(offset : Int) : Int = line.find(offset, false); + def lineToOffset(index : Int) : Int = line.find(index , true); + + def beginsWith(offset : Int, text : String): Boolean = { + var idx = 0; + while (idx < text.length()) { + if (offset + idx >= content.length) return false; + if (content(offset + idx) != text.charAt(idx)) return false; + idx = idx + 1; + } + return true; + } + def path = getFile().getPath(); + + def skipWhitespace(offset : Int): Int = + if (Character.isWhitespace(content(offset))) skipWhitespace(offset + 1) else offset; + + + def lineToString(index : Int) = { + var offset = lineToOffset(index); + val buf = new StringBuffer(); + while (!isLineBreak(offset) && offset < content.length) { + buf.append(content(offset)); + offset = offset + 1; + } + buf.toString(); + } + + + private def normalize(input : Array[char]): Array[char] = + if (input.length > 0 && input(input.length - 1) == SU) input; + else { + val content = new Array[char](input.length + 1); + System.arraycopy(input, 0, content, 0, input.length); + content(input.length) = SU; + content; + } + + + + +} diff --git a/src/compiler/scala/tools/nsc/util/Statistics.scala b/src/compiler/scala/tools/nsc/util/Statistics.scala new file mode 100644 index 0000000000..2034b07541 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/Statistics.scala @@ -0,0 +1,45 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.util; + +object Statistics { + final val enabled = false; +} + +abstract class Statistics { + + val global: Global; + import global._; + + def print(phase: Phase) = { + inform("*** Cumulative statistics at phase " + phase); + inform("#tree nodes : " + nodeCount); + inform("#identifiers : " + analyzer.idcnt); + inform("#selections : " + analyzer.selcnt); + inform("#applications: " + analyzer.appcnt); + inform("#implicits : " + analyzer.implcnt); + inform("ms implicits : " + analyzer.impltime); + inform("#uniquetypes : " + uniqueTypeCount); + inform("#symbols : " + symbolCount); + inform("#type symbols: " + typeSymbolCount); + inform("#class symbols: " + classSymbolCount); + inform("#singleton closures: " + singletonClosureCount); + inform("#compound closures : " + compoundClosureCount); + inform("#typeref closures : " + typerefClosureCount); + inform("#findMember : " + findMemberCount); + inform("#notfound member: " + noMemberCount); + inform("#mulitple member: " + multMemberCount); + inform("time findMember: " + findMemberMillis); + inform("#norm meth : " + analyzer.normM); + inform("#norm poly : " + analyzer.normP); + inform("#norm other: " + analyzer.normO); + inform("#subtype : " + subtypeCount); + inform("ms subtype: " + subtypeMillis); + } +} + diff --git a/src/compiler/scala/tools/nsc/util/TreeSet.scala b/src/compiler/scala/tools/nsc/util/TreeSet.scala new file mode 100644 index 0000000000..552f06b3f4 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/TreeSet.scala @@ -0,0 +1,52 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.util; + +/** Sets implemented as binary trees. + */ +class TreeSet[T <: AnyRef](less: (T, T) => boolean) extends Set[T] { + + private class Tree(val elem: T) { + var l: Tree = null; + var r: Tree = null; + } + + private var tree: Tree = null; + + def findEntry(x: T): T = { + def find(t: Tree): T = { + if (t == null) null + else if (less(x, t.elem)) find(t.l) + else if (less(t.elem, x)) find(t.r) + else t.elem + } + find(tree) + } + + def addEntry(x: T): unit = { + def add(t: Tree): Tree = { + if (t == null) new Tree(x) + else if (less(x, t.elem)) { t.l = add(t.l); t } + else if (less(t.elem, x)) { t.r = add(t.r); t } + else t + } + tree = add(tree) + } + + def elements = { + def elems(t: Tree): Iterator[T] = { + var it = Iterator.single(t.elem); + if (t.l != null) it = elems(t.l) append it; + if (t.r != null) it = it append elems(t.r); + it + } + if (tree == null) Iterator.empty else elems(tree) + } + + override def toString(): String = { + if (tree == null) "<empty>" else "(..." + tree.elem + "...)"; + } +} |