summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc
diff options
context:
space:
mode:
authorGilles Dubochet <gilles.dubochet@epfl.ch>2005-12-19 13:49:03 +0000
committerGilles Dubochet <gilles.dubochet@epfl.ch>2005-12-19 13:49:03 +0000
commitac849228490d5a0e2d3f048d649297d5c59b6ade (patch)
tree6314f2c06f37e67dec5827c3f94e25cf844a085c /src/compiler/scala/tools/nsc
parentd6c0efe5b4b89a0337f1cdcdabf8c607d81f4ae1 (diff)
downloadscala-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')
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala37
-rw-r--r--src/compiler/scala/tools/nsc/CompilerCommand.scala66
-rw-r--r--src/compiler/scala/tools/nsc/CompilerRun.scala20
-rw-r--r--src/compiler/scala/tools/nsc/EvalLoop.scala19
-rw-r--r--src/compiler/scala/tools/nsc/FatalError.scala8
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala518
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala234
-rw-r--r--src/compiler/scala/tools/nsc/Main.scala71
-rw-r--r--src/compiler/scala/tools/nsc/MainInterpreter.scala74
-rw-r--r--src/compiler/scala/tools/nsc/MainTokenMetric.scala61
-rw-r--r--src/compiler/scala/tools/nsc/NoPhase.scala11
-rw-r--r--src/compiler/scala/tools/nsc/Phase.scala35
-rw-r--r--src/compiler/scala/tools/nsc/Settings.scala166
-rw-r--r--src/compiler/scala/tools/nsc/SubComponent.scala29
-rw-r--r--src/compiler/scala/tools/nsc/ant/NSC.scala645
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala631
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala158
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeInfo.scala187
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreePrinters.scala303
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala1251
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala583
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala1808
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala908
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala349
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala25
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Tokens.scala97
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala289
-rw-r--r--src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala736
-rw-r--r--src/compiler/scala/tools/nsc/backend/WorklistAlgorithm.scala50
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala269
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/CheckerError.scala11
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Checkers.scala591
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala49
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala1672
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/ICodes.scala31
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala89
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Members.scala241
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala473
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Primitives.scala239
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Printers.scala128
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala352
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala96
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala1054
-rw-r--r--src/compiler/scala/tools/nsc/matching/AlgebraicMatchers.scala172
-rw-r--r--src/compiler/scala/tools/nsc/matching/Autom2.scala196
-rw-r--r--src/compiler/scala/tools/nsc/matching/BerrySethis.scala800
-rw-r--r--src/compiler/scala/tools/nsc/matching/CodeFactory.scala260
-rw-r--r--src/compiler/scala/tools/nsc/matching/DetWordAutoms.scala884
-rw-r--r--src/compiler/scala/tools/nsc/matching/LeftTracers.scala232
-rw-r--r--src/compiler/scala/tools/nsc/matching/MatcherLabels.scala126
-rw-r--r--src/compiler/scala/tools/nsc/matching/NondetWordAutoms.scala522
-rw-r--r--src/compiler/scala/tools/nsc/matching/Npair.scala64
-rw-r--r--src/compiler/scala/tools/nsc/matching/PatternMatchers.scala992
-rw-r--r--src/compiler/scala/tools/nsc/matching/PatternNodeCreator.scala108
-rw-r--r--src/compiler/scala/tools/nsc/matching/PatternNodes.scala371
-rw-r--r--src/compiler/scala/tools/nsc/matching/RightTracers.scala537
-rw-r--r--src/compiler/scala/tools/nsc/matching/SequenceMatchers.scala173
-rw-r--r--src/compiler/scala/tools/nsc/matching/StateSetComparator.scala34
-rw-r--r--src/compiler/scala/tools/nsc/matching/TransMatcher.scala301
-rw-r--r--src/compiler/scala/tools/nsc/matching/WordAutoms.scala146
-rw-r--r--src/compiler/scala/tools/nsc/models/Models.scala.xxx189
-rw-r--r--src/compiler/scala/tools/nsc/models/SemanticTokens.scala513
-rw-r--r--src/compiler/scala/tools/nsc/models/Signatures.scala65
-rw-r--r--src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala55
-rw-r--r--src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala133
-rw-r--r--src/compiler/scala/tools/nsc/reporters/Reporter.scala46
-rw-r--r--src/compiler/scala/tools/nsc/reporters/ReporterTimer.scala27
-rw-r--r--src/compiler/scala/tools/nsc/reporters/StoreReporter.scala38
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Constants.scala191
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala417
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Flags.scala191
-rw-r--r--src/compiler/scala/tools/nsc/symtab/InfoTransformers.scala45
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Names.scala332
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Scopes.scala249
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala356
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala192
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolTable.scala50
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala1055
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala2060
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileConstants.scala42
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala402
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/MetaParser.scala153
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala118
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/PickleFormat.scala99
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala244
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/SymblfileParser.scala31
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala260
-rw-r--r--src/compiler/scala/tools/nsc/transform/AddInterfaces.scala258
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala163
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala569
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala330
-rw-r--r--src/compiler/scala/tools/nsc/transform/Flatten.scala112
-rw-r--r--src/compiler/scala/tools/nsc/transform/InfoTransform.scala31
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala352
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala412
-rw-r--r--src/compiler/scala/tools/nsc/transform/OverridingPairs.scala126
-rw-r--r--src/compiler/scala/tools/nsc/transform/SampleTransform.scala45
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala286
-rw-r--r--src/compiler/scala/tools/nsc/transform/Transform.scala26
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala365
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Analyzer.scala43
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Codification.scala207
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala145
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala330
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala73
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala657
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala576
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala589
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala89
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala133
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala139
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala1567
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Variances.scala83
-rw-r--r--src/compiler/scala/tools/nsc/util/CharArrayReader.scala99
-rw-r--r--src/compiler/scala/tools/nsc/util/FreshNameCreator.scala33
-rw-r--r--src/compiler/scala/tools/nsc/util/HashSet.scala58
-rw-r--r--src/compiler/scala/tools/nsc/util/LinkedList.scala11
-rw-r--r--src/compiler/scala/tools/nsc/util/ListBuffer.scala58
-rw-r--r--src/compiler/scala/tools/nsc/util/NameTransformer.scala96
-rw-r--r--src/compiler/scala/tools/nsc/util/Position.scala79
-rw-r--r--src/compiler/scala/tools/nsc/util/Set.scala23
-rw-r--r--src/compiler/scala/tools/nsc/util/ShowPickled.scala161
-rw-r--r--src/compiler/scala/tools/nsc/util/SourceFile.scala136
-rw-r--r--src/compiler/scala/tools/nsc/util/Statistics.scala45
-rw-r--r--src/compiler/scala/tools/nsc/util/TreeSet.scala52
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 + "...)";
+ }
+}