summaryrefslogtreecommitdiff
path: root/sources
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2003-02-13 14:41:36 +0000
committerMartin Odersky <odersky@gmail.com>2003-02-13 14:41:36 +0000
commit4177daab2f54bdb20c71f623296a8bb32616fd12 (patch)
tree23f08b43f3758e825d5965b336030603a65bbcf7 /sources
parent33d6e170c97ca7b2f991896a0729941a7240b6d6 (diff)
downloadscala-4177daab2f54bdb20c71f623296a8bb32616fd12.tar.gz
scala-4177daab2f54bdb20c71f623296a8bb32616fd12.tar.bz2
scala-4177daab2f54bdb20c71f623296a8bb32616fd12.zip
Initial version.
Diffstat (limited to 'sources')
-rw-r--r--sources/scalac/ApplicationError.java87
-rw-r--r--sources/scalac/CompilerCommand.java221
-rw-r--r--sources/scalac/Global.java354
-rw-r--r--sources/scalac/Main.java38
-rw-r--r--sources/scalac/Phase.java45
-rw-r--r--sources/scalac/PhaseDescriptor.java169
-rw-r--r--sources/scalac/PhaseRepository.java96
-rw-r--r--sources/scalac/Unit.java123
-rw-r--r--sources/scalac/ast/AbstractTreeCopyFactory.java30
-rw-r--r--sources/scalac/ast/LazyTreeFactory.java430
-rw-r--r--sources/scalac/ast/Transformer.java401
-rw-r--r--sources/scalac/ast/Traverser.java215
-rw-r--r--sources/scalac/ast/Tree.java771
-rw-r--r--sources/scalac/ast/TreeCopyFactory.java165
-rw-r--r--sources/scalac/ast/TreeCreator.java293
-rw-r--r--sources/scalac/ast/TreeFactory.java155
-rw-r--r--sources/scalac/ast/TreeGen.java562
-rw-r--r--sources/scalac/ast/TreeInfo.java136
-rw-r--r--sources/scalac/ast/TreeList.java71
-rw-r--r--sources/scalac/ast/parser/Parser.java1704
-rw-r--r--sources/scalac/ast/parser/ParserPhase.java63
-rw-r--r--sources/scalac/ast/parser/Scanner.java793
-rw-r--r--sources/scalac/ast/parser/SourceRepresentation.java205
-rw-r--r--sources/scalac/ast/parser/Sourcefile.java304
-rw-r--r--sources/scalac/ast/parser/TokenData.java44
-rw-r--r--sources/scalac/ast/parser/Tokens.java84
-rw-r--r--sources/scalac/ast/printer/HTMLTreePrinter.java173
-rw-r--r--sources/scalac/ast/printer/TextTreePrinter.java683
-rw-r--r--sources/scalac/ast/printer/TreePrinter.java34
-rw-r--r--sources/scalac/checkers/CheckOwners.java168
-rw-r--r--sources/scalac/checkers/CheckSymbols.java36
-rw-r--r--sources/scalac/checkers/CheckTypes.java30
-rw-r--r--sources/scalac/checkers/Checker.java41
-rw-r--r--sources/scalac/symtab/Definitions.java421
-rw-r--r--sources/scalac/symtab/Kinds.java28
-rw-r--r--sources/scalac/symtab/Modifiers.java135
-rw-r--r--sources/scalac/symtab/NameMangler.java33
-rw-r--r--sources/scalac/symtab/Scope.java306
-rw-r--r--sources/scalac/symtab/SourceCompleter.java53
-rw-r--r--sources/scalac/symtab/SymSet.java89
-rw-r--r--sources/scalac/symtab/Symbol.java1291
-rw-r--r--sources/scalac/symtab/Type.java2353
-rw-r--r--sources/scalac/symtab/TypeTags.java47
-rw-r--r--sources/scalac/symtab/classfile/AttributeParser.java439
-rw-r--r--sources/scalac/symtab/classfile/ClassParser.java111
-rw-r--r--sources/scalac/symtab/classfile/ClassfileConstants.java57
-rw-r--r--sources/scalac/symtab/classfile/ClassfileParser.java239
-rw-r--r--sources/scalac/symtab/classfile/ConstantPool.java183
-rw-r--r--sources/scalac/symtab/classfile/JavaTypeCreator.java82
-rw-r--r--sources/scalac/symtab/classfile/JavaTypeFactory.java28
-rw-r--r--sources/scalac/symtab/classfile/PackageParser.java129
-rw-r--r--sources/scalac/symtab/classfile/Signatures.java122
-rw-r--r--sources/scalac/transformer/LambdaLift.java508
-rw-r--r--sources/scalac/transformer/LambdaLiftPhase.java135
-rw-r--r--sources/scalac/transformer/OwnerTransformer.java132
-rw-r--r--sources/scalac/transformer/UnCurry.java180
-rw-r--r--sources/scalac/transformer/UnCurryPhase.java117
-rw-r--r--sources/scalac/typechecker/Analyzer.java1975
-rw-r--r--sources/scalac/typechecker/AnalyzerPhase.java103
-rw-r--r--sources/scalac/typechecker/Context.java63
-rw-r--r--sources/scalac/typechecker/DeSugarize.java681
-rw-r--r--sources/scalac/typechecker/ImportList.java64
-rw-r--r--sources/scalac/typechecker/Infer.java814
-rw-r--r--sources/scalac/typechecker/RefCheck.java208
-rw-r--r--sources/scalac/typechecker/RefCheckPhase.java42
-rw-r--r--sources/scalac/util/AbstractFile.java557
-rw-r--r--sources/scalac/util/AbstractFileReader.java107
-rw-r--r--sources/scalac/util/ArrayApply.java54
-rw-r--r--sources/scalac/util/ClassPath.java161
-rw-r--r--sources/scalac/util/Debug.java465
-rw-r--r--sources/scalac/util/FreshNameCreator.java59
-rw-r--r--sources/scalac/util/Name.java391
-rw-r--r--sources/scalac/util/NameTransformer.java111
-rw-r--r--sources/scalac/util/Names.java98
-rw-r--r--sources/scalac/util/OptionParser.java546
-rw-r--r--sources/scalac/util/Position.java54
-rw-r--r--sources/scalac/util/PrefixMatcher.java114
-rw-r--r--sources/scalac/util/Reporter.java192
-rw-r--r--sources/scalac/util/SourceRepresentation.java205
-rw-r--r--sources/scalac/util/Strings.java102
-rw-r--r--sources/scalac/util/UniqueID.java30
81 files changed, 23138 insertions, 0 deletions
diff --git a/sources/scalac/ApplicationError.java b/sources/scalac/ApplicationError.java
new file mode 100644
index 0000000000..e689217eec
--- /dev/null
+++ b/sources/scalac/ApplicationError.java
@@ -0,0 +1,87 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+import scalac.util.Debug;
+
+public class ApplicationError extends Error {
+
+ //########################################################################
+ // Private state
+
+ private final Throwable cause; // !!! remove and use API 1.4
+
+ //########################################################################
+ // Private interface
+
+ private static String combine(String message, Object object) {
+ String string = Debug.show(object);
+ return message == null ? string : message + ": " + string;
+ }
+
+ //########################################################################
+ // ApplicationError constructors
+
+ public ApplicationError() {
+ this((String)null, (Throwable)null);
+ }
+
+ public ApplicationError(String message) {
+ this(message, (Throwable)null);
+ }
+
+ public ApplicationError(Object object) {
+ this(null, object, null);
+ }
+
+ public ApplicationError(Throwable cause) {
+ this((String)null, cause);
+ }
+
+ public ApplicationError(String message, Object object) {
+ this(message, object, null);
+ }
+
+ public ApplicationError(String message, Throwable cause) {
+ super(message);
+ this.cause = cause;
+ }
+
+ public ApplicationError(Object object, Throwable cause) {
+ this(null, object, cause);
+ }
+
+ public ApplicationError(String message, Object object, Throwable cause) {
+ this(combine(message, object), cause);
+ }
+
+ //########################################################################
+ // Throwable interface
+
+ public void printStackTrace(PrintStream printer) {
+ super.printStackTrace(printer);
+ if (cause != null) {
+ printer.print("Caused by: ");
+ cause.printStackTrace(printer);
+ }
+ }
+
+ public void printStackTrace(PrintWriter printer) {
+ super.printStackTrace(printer);
+ if (cause != null) {
+ printer.print("Caused by: ");
+ cause.printStackTrace(printer);
+ }
+ }
+
+ //########################################################################
+}
diff --git a/sources/scalac/CompilerCommand.java b/sources/scalac/CompilerCommand.java
new file mode 100644
index 0000000000..743187e81f
--- /dev/null
+++ b/sources/scalac/CompilerCommand.java
@@ -0,0 +1,221 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import scalac.util.ClassPath;
+import scalac.util.Reporter;
+import scalac.util.CommandParser;
+import scalac.util.ArgumentParser;
+import scalac.util.OptionParser;
+import scalac.util.BooleanOptionParser;
+import scalac.util.StringOptionParser;
+import scalac.util.ChoiceOptionParser;
+import scalac.util.HelpOptionParser;
+import scalac.util.VersionOptionParser;
+//import scalac.util.OptimizeOptionParser;
+import scalac.util.PhaseSetOptionParser;
+import scalac.util.PrintOptionParser;
+import scalac.util.UnknownOptionParser;
+import scalac.util.ScalaFileArgumentParser;
+import scalac.util.UnknownArgumentParser;
+import scalac.util.Strings;
+
+public class CompilerCommand extends CommandParser {
+
+ public final PhaseRepository phases;
+
+ public final BooleanOptionParser nowarn;
+ public final BooleanOptionParser verbose;
+ public final BooleanOptionParser debug;
+ public final BooleanOptionParser uniqid;
+ public final BooleanOptionParser types;
+ public final BooleanOptionParser prompt;
+ //public final OptimizeOptionParser optimize;
+ public final StringOptionParser classpath;
+ public final StringOptionParser sourcepath;
+ public final StringOptionParser bootclasspath;
+ public final StringOptionParser extdirs;
+ public final StringOptionParser outpath;
+ public final ChoiceOptionParser target;
+ public final BooleanOptionParser noimports;
+ public final StringOptionParser jaco;
+ public final PhaseSetOptionParser skip;
+ public final PhaseSetOptionParser check;
+ public final PrintOptionParser print;
+ public final ChoiceOptionParser printer;
+ public final StringOptionParser printfile;
+ public final PhaseSetOptionParser graph;
+ public final PhaseSetOptionParser stop;
+ public final PhaseSetOptionParser log;
+ public final VersionOptionParser version;
+ public final HelpOptionParser help;
+ public final UnknownOptionParser unknown_options;
+ public final ScalaFileArgumentParser files;
+ public final UnknownArgumentParser unknown_arguments;
+
+ public CompilerCommand(String product, String version,
+ Reporter reporter, PhaseRepository phases)
+ {
+ this(product, version, "<source files>", reporter, phases);
+ }
+
+ protected CompilerCommand(String product, String version, String syntax,
+ Reporter reporter, PhaseRepository phases)
+ {
+ super(product, version, syntax, reporter);
+ this.phases = phases;
+
+ ArgumentParser[] parsers = new ArgumentParser[] {
+
+ this.nowarn = new BooleanOptionParser(this,
+ "nowarn", "Generate no warnings",
+ false),
+
+ this.verbose = new BooleanOptionParser(this,
+ "verbose", "Output messages about what the compiler is doing",
+ false),
+
+ this.debug = new BooleanOptionParser(this,
+ "debug", "Output debugging messages",
+ false),
+
+ this.uniqid = new BooleanOptionParser(this,
+ "uniqid", "Print identifiers with unique names (debugging option)",
+ false),
+
+ this.types = new BooleanOptionParser(this,
+ "types", "Print tree types (debugging option)",
+ false),
+
+ this.prompt = new BooleanOptionParser(this,
+ "prompt", "Display a prompt after each error (debugging option)",
+ false),
+
+ //this.optimize = new OptimizeOptionParser(this,
+ // "optimize", "optimize bytecode (-optimize:help for option list)",
+ // null /* todo: uncomment: phases.OPTIMIZE */),
+
+ this.classpath = new StringOptionParser(this,
+ "classpath", "Specify where to find user class files",
+ "path", ClassPath.CLASS_PATH),
+
+ this.sourcepath = new StringOptionParser(this,
+ "sourcepath", "Specify where to find input source files",
+ "path", ClassPath.SOURCE_PATH),
+
+ this.bootclasspath = new StringOptionParser(this,
+ "bootclasspath", "Override location of bootstrap class files",
+ "path", ClassPath.BOOT_PATH),
+
+ this.extdirs = new StringOptionParser(this,
+ "extdirs", "Override location of installed extensions",
+ "dirs", ClassPath.EXTENSION_PATH),
+
+ this.outpath = new StringOptionParser(this,
+ "d", "Specify where to place generated class files",
+ "directory", "."),
+
+ this.target = new ChoiceOptionParser(this,
+ "target", "Specify which bakend to use",
+ "target", Global.TARGETS, Global.TARGET_JVM),
+
+ this.noimports = new BooleanOptionParser(this,
+ "noimports", "Compile without any implicit imports",
+ false),
+
+ this.jaco = new StringOptionParser(this,
+ "jaco", "Pass argument to jaco backend (debugging option)",
+ "argument", null),
+
+ this.skip = new PhaseSetOptionParser(this,
+ "skip", "Skip <phases> (see below)",
+ phases.phases, PhaseDescriptor.SKIP),
+
+ this.check = new PhaseSetOptionParser(this,
+ "check", "Check the tree after <phases> (see below)",
+ phases.phases, PhaseDescriptor.CHECK),
+
+ this.print = new PrintOptionParser(this,
+ "print", "Print out program after <phases> (see below)",
+ phases.phases, PhaseDescriptor.PRINT),
+
+ this.printer = new ChoiceOptionParser(this,
+ "printer", "Printer to use",
+ "printer", Global.PRINTERS, Global.PRINTER_TEXT),
+
+ this.printfile = new StringOptionParser(this,
+ "print-file", "Specify file in which to print trees",
+ "file", "-"),
+
+ this.graph = new PhaseSetOptionParser(this,
+ "graph", "Graph the program after <phases> (see below)",
+ phases.phases, PhaseDescriptor.GRAPH),
+
+ this.stop = new PhaseSetOptionParser(this,
+ "stop", "Stop after first phase in <phases> (see below)",
+ phases.phases, PhaseDescriptor.STOP),
+
+ this.log = new PhaseSetOptionParser(this,
+ "log", "Log operations in <phases> (see below)",
+ phases.phases, PhaseDescriptor.LOG),
+
+ this.version = new VersionOptionParser(this,
+ "version", "Print product version and exit",
+ product() + " " + version() + " -- (c) 2002 LAMP/EPFL"),
+
+ this.help = new HelpOptionParser(this,
+ "help", "Print a synopsis of standard options"),
+
+ this.unknown_options = new UnknownOptionParser(this),
+
+ this.files = new ScalaFileArgumentParser(this),
+
+ this.unknown_arguments = new UnknownArgumentParser(this),
+
+ };
+
+ for (int i = 0; i < parsers.length; i++) add(parsers[i]);
+ }
+
+ public boolean parse(String[] args) {
+ boolean result = super.parse(args);
+ reporter().nowarn = nowarn.value;
+ reporter().verbose = verbose.value;
+ reporter().prompt = prompt.value;
+ return result;
+ }
+
+ public String getHelpMessage() {
+ StringBuffer buffer = new StringBuffer(super.getHelpMessage());
+ buffer.append(Strings.EOL);
+ buffer.append("and possible compilation phases include:");
+ buffer.append(Strings.EOL);
+ List lines = new ArrayList(phases.phases.length);
+ for (int i = 0; i < phases.phases.length; i++) {
+ PhaseDescriptor phase = phases.phases[i];
+ lines.add(" " + phase.name() + "\t " + phase.description());
+ }
+ lines.add(" " + "all" + "\t " + "matches all phases");
+ buffer.append(Strings.format(lines));
+ return buffer.toString();
+ }
+
+ public ClassPath classpath() {
+ return new ClassPath(classpath.value, sourcepath.value,
+ bootclasspath.value, extdirs.value);
+ }
+
+ public String outpath() {
+ return outpath.value + (outpath.value.endsWith("/") ? "" : "/");
+ }
+
+}
diff --git a/sources/scalac/Global.java b/sources/scalac/Global.java
new file mode 100644
index 0000000000..3c3d73c0e6
--- /dev/null
+++ b/sources/scalac/Global.java
@@ -0,0 +1,354 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac;
+
+import java.io.*;
+import java.util.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.ast.parser.*;
+import scalac.symtab.Definitions;
+import scalac.ast.printer.*;
+
+
+/** The global environment of a compiler run
+ *
+ * @author Matthias Zenger
+ * @version 1.0
+ */
+public class Global {
+
+ public static Global instance;
+
+ /** global options
+ */
+ public final boolean noimports;
+ //public final boolean optimize;
+ public final boolean debug;
+ public final boolean uniqid;
+
+ public final boolean printtypes;
+ public final boolean printtokens;
+
+ public final String outpath;
+ public final String target;
+
+ public final String jacoDebug;
+
+ /** the message reporter
+ */
+ public final Reporter reporter;
+
+ /** a stack for maintaining timestamps
+ */
+ private final Stack startTimes = new Stack();
+
+ /** all compilation units
+ */
+ public Unit[] units;
+
+ /** the class path
+ */
+ public final ClassPath classPath;
+
+ /** the global tree factory
+ */
+ public final TreeFactory make;
+
+ /** the fresh name creator
+ */
+ public final FreshNameCreator freshNameCreator;
+
+ /** the tree generator
+ */
+ public final TreeGen treeGen;
+
+ /** the unique-id generator
+ */
+ public final UniqueID uniqueID;
+
+ /** the global tree printer
+ */
+ public final TreePrinter printer;
+ public OutputStream printStream;
+ public final TreePrinter debugPrinter;
+
+ /** the current phase
+ */
+ public PhaseDescriptor currentPhase;
+
+ /** the global definitions
+ */
+ public Definitions definitions;
+
+ /** the global primitives
+ */
+ //public Primitives primitives;
+
+ /** compilation phases.
+ */
+ public final PhaseRepository PHASE;
+ public final PhaseDescriptor[] phases;
+
+ public final int POST_ANALYZER_PHASE_ID = 3;
+
+ /** compilation targets
+ */
+ public static final String TARGET_INT;
+ public static final String TARGET_JAVA;
+ public static final String TARGET_JVM;
+ public static final String TARGET_MSIL;
+
+ public static final String[] TARGETS = new String[] {
+ TARGET_INT = "int".intern(),
+ TARGET_JAVA = "java".intern(),
+ TARGET_JVM = "jvm".intern(),
+ TARGET_MSIL = "msil".intern(),
+ };
+
+ /** tree printers
+ */
+ public static final String PRINTER_TEXT;
+ public static final String PRINTER_HTML;
+
+ public static final String[] PRINTERS = new String[] {
+ PRINTER_TEXT = "text".intern(),
+ PRINTER_HTML = "html".intern(),
+ };
+
+ public Global(CompilerCommand args) {
+ this(args, false);
+ }
+
+ public Global(CompilerCommand args, boolean interpret) {
+ if (Global.instance != null) { // jaco bug: can't use assert here
+ new Error("Duplicate creation of Global").printStackTrace();
+ System.exit(1);
+ };
+ Global.instance = this;
+ this.reporter = args.reporter();
+ this.start(); // timestamp to compute the total time
+ this.noimports = args.noimports.value;
+ //this.optimize = args.optimize.optimize;
+ this.debug = args.debug.value;
+ this.uniqid = args.uniqid.value;
+ this.printtypes = args.types.value;
+ this.printtokens = args.print.tokens;
+ this.classPath = args.classpath();
+ this.outpath = args.outpath();
+ this.target = interpret ? TARGET_INT : args.target.value.intern();
+ this.jacoDebug = args.jaco.value;
+ this.uniqueID = new UniqueID();
+ String printFile = args.printfile.value;
+ try {
+ this.printStream = "-".equals(printFile)
+ ? System.out
+ : new FileOutputStream(printFile);
+ } catch (FileNotFoundException e) {
+ error("unable to open file " + printFile + ". Printing on console");
+ this.printStream = System.out;
+ }
+ String printerName = args.printer.value.intern();
+ if (printerName == PRINTER_TEXT)
+ this.printer = new TextTreePrinter(printStream);
+ else
+ this.printer = new HTMLTreePrinter(printStream);
+ this.debugPrinter = new TextTreePrinter(System.err);
+ this.freshNameCreator = new FreshNameCreator();
+ this.make = new TreeCreator();
+ this.currentPhase = PhaseDescriptor.INITIAL;
+ this.definitions = new Definitions(this);
+ //this.primitives = new Primitives(this);
+ this.treeGen = new TreeGen(this, make);
+ this.PHASE = args.phases;
+ List phases = new ArrayList();
+ phases.add(PHASE.INITIAL);
+ phases.add(PHASE.PARSER);
+ phases.add(PHASE.ANALYZER);
+ phases.add(PHASE.REFCHECK);
+ phases.add(PHASE.UNCURRY);
+ /*
+ if (optimize) {
+ phases.add(PHASE.OPTIMIZE);
+ }
+ phases.add(PHASE.TRANSMATCH);
+ */
+ //phases.add(PHASE.LAMBDALIFT);
+ /*
+ phases.add(PHASE.EXPLICITOUTER);
+ phases.add(PHASE.ADDACCESSORS);
+ phases.add(PHASE.ADDINTERFACES);
+ phases.add(PHASE.EXPANDMIXIN);
+ phases.add(PHASE.ERASURE);
+ if (target == TARGET_INT || target == TARGET_MSIL || target == TARGET_JVM) {
+ phases.add(PHASE.ADDCONSTRUCTORS);
+ }
+ if (target == TARGET_JAVA) phases.add(PHASE.GENJAVA);
+ if (target == TARGET_MSIL) phases.add(PHASE.GENMSIL);
+ if (target == TARGET_JVM) phases.add(PHASE.GENJVM);
+ */
+ phases.add(PHASE.TERMINAL);
+ this.phases = new PhaseDescriptor[phases.size()];
+ for (int i = 0; i < phases.size(); i++) {
+ PhaseDescriptor phase = (PhaseDescriptor)phases.get(i);
+ this.phases[i] = phase;
+ if (i > 0) this.phases[i - 1].flags |= phase.flags >>> 16;
+ phase.initialize(this, i);
+ assert phase.id == i;
+ }
+ assert PHASE.ANALYZER.id + 1 == POST_ANALYZER_PHASE_ID;
+ }
+
+ /** Move to next phase
+ */
+ public void nextPhase() {
+ currentPhase = phases[currentPhase.id + 1];
+ }
+
+ /** Move to previous phase
+ */
+ public void prevPhase() {
+ currentPhase = phases[currentPhase.id - 1];
+ }
+
+ /** the top-level compilation process
+ */
+ public void compile(String[] files, boolean console) {
+ reporter.resetCounters();
+ // parse files
+ List units = new ArrayList(files.length);
+ for (int i = 0; i < files.length; i++) {
+ String file = files[i];
+ try {
+ units.add(new Unit(this, new Sourcefile(file, console)));
+ } catch (FileNotFoundException e) {
+ error("file " + file + " not found");
+ } catch (IOException e) {
+ error(e.toString());
+ }
+ }
+ this.units = (Unit[])units.toArray(new Unit[units.size()]);
+ compile();
+ }
+
+ /** the top-level compilation process
+ */
+ public void compile(String input, boolean console) {
+ reporter.resetCounters();
+ Sourcefile source = new Sourcefile(input.getBytes(), console);
+ units = new Unit[]{new Unit(this, source)};
+ compile();
+ }
+
+ /** compile all compilation units
+ */
+ private void compile() {
+ printer.begin();
+
+ // apply successive phases and pray that it works
+ for (int i = 0; i < phases.length && reporter.errors() == 0; ++i) {
+ currentPhase = phases[i];
+ if ((currentPhase.flags & PhaseDescriptor.SKIP) != 0) {
+ operation("skipping phase " + currentPhase.name());
+ } else {
+ currentPhase.apply(this);
+ }
+ if ((currentPhase.flags & PhaseDescriptor.PRINT) != 0)
+ currentPhase.print(this);
+ if ((currentPhase.flags & PhaseDescriptor.GRAPH) != 0)
+ currentPhase.graph(this);
+ if ((currentPhase.flags & PhaseDescriptor.CHECK) != 0)
+ currentPhase.check(this);
+ if ((currentPhase.flags & PhaseDescriptor.STOP) != 0) {
+ operation("stopped after phase " + currentPhase.name());
+ break;
+ }
+ }
+
+ printer.end();
+ }
+
+ /** stop the compilation process immediately
+ */
+ public Error fail() {
+ throw new ApplicationError();
+ }
+
+ /** stop the compilation process immediately
+ */
+ public Error fail(String message) {
+ throw new ApplicationError(message);
+ }
+
+ /** stop the compilation process immediately
+ */
+ public Error fail(String message, Object object) {
+ throw new ApplicationError(message, object);
+ }
+
+ /** stop the compilation process immediately
+ */
+ public Error fail(Object value) {
+ throw new ApplicationError(value);
+ }
+
+ /** issue a global error
+ */
+ public void error(String message) {
+ reporter.error("error: " + message);
+ }
+
+ /** issue a global warning
+ */
+ public void warning(String message) {
+ reporter.warning("warning: " + message);
+ }
+
+ /** issue a global note (if in verbose mode)
+ */
+ public void note(String message) {
+ reporter.note("note: " + message);
+ }
+
+ /** issue an operation note
+ */
+ public void operation(String message) {
+ reporter.inform("[" + message + "]");
+ }
+
+ /** issue a debug message from currentPhase
+ */
+ // the boolean return value is here to let one write "assert log( ... )"
+ public boolean log(String message) {
+ if (log()) {
+ reporter.report("[log " + currentPhase.name() + "] " + message);
+ }
+ return true;
+ }
+
+ /** return true if logging is switched on for the current phase
+ */
+ public boolean log() {
+ return (currentPhase.flags & PhaseDescriptor.LOG) != 0;
+ }
+
+ /** start a new timer
+ */
+ public void start() {
+ startTimes.push(new Long(System.currentTimeMillis()));
+ }
+
+ /** issue timing information
+ */
+ public void stop(String message) {
+ long start = ((Long)startTimes.pop()).longValue();
+ reporter.inform("[" + message + " in " +
+ (System.currentTimeMillis() - start) + "ms]");
+ }
+}
diff --git a/sources/scalac/Main.java b/sources/scalac/Main.java
new file mode 100644
index 0000000000..2c0dc78140
--- /dev/null
+++ b/sources/scalac/Main.java
@@ -0,0 +1,38 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac;
+
+import scalac.util.Reporter;
+
+/** The main class for SoCoS, a compiler for the programming
+ * language Scala.
+ *
+ * @author Matthias Zenger
+ * @version 1.0
+ */
+public class Main {
+
+ public static final String PRODUCT =
+ System.getProperty("scala.product", "socos");
+ public static final String VERSION =
+ System.getProperty("scala.version", "unknown version");
+
+ public static void main(String[] args) {
+ Reporter reporter = new Reporter();
+ CompilerCommand command = new CompilerCommand(
+ PRODUCT, VERSION, reporter, new PhaseRepository());
+ if (command.parse(args) && command.files.list.size() > 0) {
+ Global global = new Global(command);
+ global.compile(command.files.toArray(), false);
+ global.stop("total");
+ global.reporter.printSummary();
+ }
+ System.exit((reporter.errors() > 0) ? -1 : 0);
+ }
+}
diff --git a/sources/scalac/Phase.java b/sources/scalac/Phase.java
new file mode 100644
index 0000000000..bbce46e013
--- /dev/null
+++ b/sources/scalac/Phase.java
@@ -0,0 +1,45 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac;
+
+/** Representation of a compiler phase. PhaseDescriptors create
+ * phase. Phases operate subsequently on all compilation units.
+ *
+ * @author Matthias Zenger
+ * @version 1.0
+ */
+public abstract class Phase {
+
+ /** the global environment
+ */
+ public final Global global;
+
+ /** the descriptor for this phase
+ */
+ public final PhaseDescriptor descr;
+
+ /** constructor
+ */
+ public Phase(Global global, PhaseDescriptor descr) {
+ this.global = global;
+ this.descr = descr;
+ }
+
+ /** apply this phase to all compilation units
+ */
+ public void apply() {
+ for (int i = 0; i < global.units.length; i++) {
+ apply(global.units[i]);
+ }
+ }
+
+ /** apply this phase to the given compilation unit
+ */
+ public abstract void apply(Unit unit);
+}
diff --git a/sources/scalac/PhaseDescriptor.java b/sources/scalac/PhaseDescriptor.java
new file mode 100644
index 0000000000..0316a80215
--- /dev/null
+++ b/sources/scalac/PhaseDescriptor.java
@@ -0,0 +1,169 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac;
+
+import java.util.*;
+import scalac.ast.printer.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import scalac.checkers.*;
+import java.io.PrintWriter;
+
+/**
+ * Information about a compiler phase.
+ *
+ * @author Michel Schinz
+ */
+
+public abstract class PhaseDescriptor {
+
+ private static class InitialPhaseDescriptor extends PhaseDescriptor {
+
+ public String name() {
+ return "initial";
+ }
+
+ public String description() {
+ return "initializing compiler";
+ }
+
+ public Phase createPhase(Global global) {
+ return null;
+ }
+
+ /** apply phase to all compilation units
+ */
+ public void apply(Global global) {}
+ }
+
+ private static class TerminalPhaseDescriptor extends PhaseDescriptor {
+
+ public String name() {
+ return "terminal";
+ }
+
+ public String description() {
+ return "compilation terminated ";
+ }
+
+ public Phase createPhase(Global global) {
+ return null;
+ }
+
+ /** apply phase to all compilation units
+ */
+ public void apply(Global global) {}
+ }
+
+ public static PhaseDescriptor INITIAL = new InitialPhaseDescriptor();
+ public static PhaseDescriptor TERMINAL = new TerminalPhaseDescriptor();
+
+ public static final int SKIP = 0x0001;
+ public static final int CHECK = 0x0002;
+ public static final int PRINT = 0x0004;
+ public static final int GRAPH = 0x0008;
+ public static final int STOP = 0x0010;
+ public static final int LOG = 0x0020;
+
+ public int flags;
+ public int id;
+
+ /** return a short, one-word name for the phase.
+ */
+ public abstract String name();
+
+ /** return a one-line description for the phase.
+ */
+ public abstract String description();
+
+ /** a one-line task description of this phase
+ */
+ public String taskDescription() {
+ return description();
+ }
+
+ /** initialize the phase
+ */
+ public final void initialize(Global global) {
+ throw new Error();
+ }
+ public void initialize(Global global, int id) {
+ this.id = id;
+ }
+
+ /** create a phase
+ */
+ public abstract Phase createPhase(Global global);
+
+ /** Assume that `tp' is the info of symbol `sym' before this phase.
+ * Return the info of `sym' after the phase.
+ */
+ public Type transformInfo(Symbol sym, Type tp) {
+ return tp;
+ }
+
+ /** apply phase to all compilation units
+ */
+ public void apply(Global global) {
+ global.start();
+ createPhase(global).apply();
+ global.stop(taskDescription());
+ }
+
+ /** check all compilation units
+ */
+ public void check(Global global) {
+ for (int i = 0; i < global.units.length; i++)
+ check(global.units[i]);
+ }
+
+ /** print all compilation units
+ */
+ public void print(Global global) {
+ TreePrinter printer = global.printer;
+
+ printer.beginSection(1, "Trees after phase " + name());
+ for (int i = 0; i < global.units.length; i++)
+ printer.print(global.units[i]);
+ }
+
+ /** graph all compilation units
+ */
+ public void graph(Global global) {
+ for (int i = 0; i < global.units.length; i++)
+ graph(global.units[i]);
+ }
+
+ /** return an array of checkers which can be applied after the phase
+ */
+ public Checker[] postCheckers(Global global) {
+ return new Checker[0];
+ }
+
+ /** check the result of this phase for the given compilation unit
+ */
+ public void check(Unit unit) {
+ Checker[] checkers = postCheckers(unit.global);
+ for (int i = 0; i < checkers.length; i++)
+ checkers[i].traverse(unit);
+ }
+
+ /** graph the result of this phase for the given compilation unit
+ */
+ public void graph(Unit unit) {
+ /* todo: uncomment
+ new scala.compiler.gdl.TreePrinter().printInFile(
+ unit, unit.source + "-" + name() + ".gdl");
+ */
+ }
+
+ public String toString() {
+ return name();
+ }
+}
diff --git a/sources/scalac/PhaseRepository.java b/sources/scalac/PhaseRepository.java
new file mode 100644
index 0000000000..4b7e3d4343
--- /dev/null
+++ b/sources/scalac/PhaseRepository.java
@@ -0,0 +1,96 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac;
+
+import scalac.ast.parser.ParserPhase;
+import scalac.typechecker.AnalyzerPhase;
+import scalac.typechecker.RefCheckPhase;
+import scalac.transformer.UnCurryPhase;
+/*
+import scalac.transformer.TransMatchPhase;
+*/
+import scalac.transformer.LambdaLiftPhase;
+/*
+import scalac.transformer.ExplicitOuterClassesPhase;
+import scalac.transformer.AddInterfacesPhase;
+import scalac.transformer.AddAccessorsPhase;
+import scalac.transformer.ExpandMixinsPhase;
+import scalac.transformer.ErasurePhase;
+import scalac.optimizer.OptimizePhase;
+import scalac.backend.AddConstructorsPhase;
+import scalac.backend.msil.GenMSILPhase;
+import scalac.backend.jvm.GenJVMPhase;
+import scalac.jaco.GenJavaPhase;
+*/
+
+public class PhaseRepository {
+
+ //########################################################################
+ // Private state
+
+ public final PhaseDescriptor[] phases;
+
+ //########################################################################
+ // Reporter constructors
+
+ public PhaseRepository() {
+ this.phases = new PhaseDescriptor[] {
+ INITIAL = PhaseDescriptor.INITIAL,
+ PARSER = new ParserPhase(),
+ ANALYZER = new AnalyzerPhase(),
+ REFCHECK = new RefCheckPhase(),
+ UNCURRY = new UnCurryPhase(),
+ /*
+ OPTIMIZE = new OptimizePhase(),
+ TRANSMATCH = new TransMatchPhase(),
+ */
+ LAMBDALIFT = new LambdaLiftPhase(),
+ /*
+ EXPLICITOUTER = new ExplicitOuterClassesPhase(),
+ ADDACCESSORS = new AddAccessorsPhase(),
+ ADDINTERFACES = new AddInterfacesPhase(),
+ EXPANDMIXIN = new ExpandMixinsPhase(),
+ ERASURE = new ErasurePhase(),
+ ADDCONSTRUCTORS = new AddConstructorsPhase(),
+ GENMSIL = new GenMSILPhase(),
+ GENJAVA = new GenJavaPhase(),
+ GENJVM = new GenJVMPhase(),
+ */
+ TERMINAL = PhaseDescriptor.TERMINAL,
+ };
+ }
+
+ //########################################################################
+ // Reporter interface
+
+ public final PhaseDescriptor INITIAL;
+ public final ParserPhase PARSER;
+ public final AnalyzerPhase ANALYZER;
+ public final RefCheckPhase REFCHECK;
+ public final UnCurryPhase UNCURRY;
+ /*
+ public final OptimizePhase OPTIMIZE;
+ public final TransMatchPhase TRANSMATCH;
+ */
+ public final LambdaLiftPhase LAMBDALIFT;
+ /*
+ public final ExplicitOuterClassesPhase EXPLICITOUTER;
+ public final AddAccessorsPhase ADDACCESSORS;
+ public final AddInterfacesPhase ADDINTERFACES;
+ public final ExpandMixinsPhase EXPANDMIXIN;
+ public final ErasurePhase ERASURE;
+ public final AddConstructorsPhase ADDCONSTRUCTORS;
+ public final GenMSILPhase GENMSIL;
+ public final GenJavaPhase GENJAVA;
+ public final GenJVMPhase GENJVM;
+ */
+ public final PhaseDescriptor TERMINAL;
+
+ //########################################################################
+}
diff --git a/sources/scalac/Unit.java b/sources/scalac/Unit.java
new file mode 100644
index 0000000000..f9a3ec81c6
--- /dev/null
+++ b/sources/scalac/Unit.java
@@ -0,0 +1,123 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac;
+
+import scalac.util.*;
+import scalac.symtab.NameMangler;
+import scalac.ast.parser.Sourcefile;
+import scalac.ast.Tree;
+import scala.compiler.typechecker.*;
+import java.io.*;
+import java.util.*;
+
+
+/** A representation for a compilation unit in scala
+ *
+ * @author Matthias Zenger
+ * @version 1.0
+ */
+public class Unit {
+
+ /** the global compilation environment
+ */
+ public Global global;
+
+ /** the associated source code file
+ */
+ public Sourcefile source;
+
+ /** the content of the compilation unit in tree form
+ */
+ public Tree[] body;
+
+ /** the generated symbol data; Symbol -> byte[]
+ public SymData symdata;
+ */
+
+ /** the name mangler
+ */
+ public NameMangler mangler = new NameMangler();
+
+ /** number of errors issued for this compilation unit
+ */
+ public int errors;
+
+ /** number of warnings issued for this compilation unit
+ */
+ public int warnings;
+
+ /** number of notes issued for this compilation unit
+ */
+ public int notes;
+
+ public Unit(Global global, Sourcefile source) {
+ this.global = global;
+ this.source = source;
+ }
+ /*
+ public void print(String message) {
+ print(System.out, message);
+ }
+
+ public void print(PrintStream out, String message) {
+ out.println("[[begin " + message + "]]");
+ new Printer(out).printCompilationUnit(this);
+ out.println();
+ out.println("[[end " + message + "]]");
+ }
+ */
+ /** issue an error in this compilation unit
+ */
+ public void error(String message) {
+ error(Position.NOPOS, message);
+ }
+
+ /** issue an error in this compilation unit at a specific location
+ */
+ public void error(int pos, String message) {
+ boolean hidden = source.testAndSetLog(pos, message);
+ if (!hidden) errors++;
+ global.reporter.error(source.getMessage(pos, message), hidden);
+ }
+
+ /** issue a warning in this compilation unit
+ */
+ public void warning(String message) {
+ warning(Position.NOPOS, message);
+ }
+
+ /** issue a warning in this compilation unit at a specific location
+ */
+ public void warning(int pos, String message) {
+ if (global.reporter.nowarn) return;
+ message = "warning: " + message;
+ boolean hidden = source.testAndSetLog(pos, message);
+ if (!hidden) warnings++;
+ global.reporter.warning(source.getMessage(pos, message), hidden);
+ }
+
+ /** issue a note in this compilation unit
+ */
+ public void note(String message) {
+ note(Position.NOPOS, message);
+ }
+
+ /** issue a note in this compilation unit at a specific location
+ */
+ public void note(int pos, String message) {
+ global.reporter.note(source.getMessage(pos, message));
+ notes++;
+ }
+
+ /** return a string representation
+ */
+ public String toString() {
+ return source.toString();
+ }
+}
diff --git a/sources/scalac/ast/AbstractTreeCopyFactory.java b/sources/scalac/ast/AbstractTreeCopyFactory.java
new file mode 100644
index 0000000000..358d468cd4
--- /dev/null
+++ b/sources/scalac/ast/AbstractTreeCopyFactory.java
@@ -0,0 +1,30 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+\* */
+
+// $Id$
+
+package scalac.ast;
+
+import scalac.ast.*;
+
+/**
+ * Abstract superclass for all TreeCopyFactories, which provides only
+ * the code to copy the attribution from the "old" to the "new" tree.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+
+public abstract class AbstractTreeCopyFactory implements TreeCopyFactory {
+ public void attribute(Tree newTree, Tree oldTree) {
+ if (newTree != oldTree) {
+ newTree.type = oldTree.type;
+ if (newTree.hasSymbol())
+ newTree.setSymbol(oldTree.symbol());
+ }
+ }
+}
diff --git a/sources/scalac/ast/LazyTreeFactory.java b/sources/scalac/ast/LazyTreeFactory.java
new file mode 100644
index 0000000000..43cef5652a
--- /dev/null
+++ b/sources/scalac/ast/LazyTreeFactory.java
@@ -0,0 +1,430 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast;
+
+import scalac.util.Name;
+import Tree.*;
+
+public class LazyTreeFactory extends AbstractTreeCopyFactory {
+ protected final TreeFactory make;
+
+ public LazyTreeFactory(TreeFactory make) {
+ this.make = make;
+ }
+
+ public Tree Bad(Tree tree) {
+ return tree;
+ }
+
+ public Tree ClassDef(Tree tree,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl) {
+ ClassDef t = (ClassDef)tree;
+ if ((t.mods == mods) &&
+ (t.name == name) &&
+ (t.tparams == tparams) &&
+ (t.vparams == vparams) &&
+ (t.tpe == tpe) &&
+ (t.impl == impl))
+ return t;
+ tree = make.ClassDef(t.pos, mods, name, tparams, vparams, tpe, impl);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree PackageDef(Tree tree,
+ Tree packaged,
+ Template impl) {
+ PackageDef t = (PackageDef)tree;
+ if ((t.packaged == packaged) &&
+ (t.impl == impl))
+ return t;
+ tree = make.PackageDef(t.pos, packaged, impl);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree ModuleDef(Tree tree,
+ int mods,
+ Name name,
+ Tree tpe,
+ Template impl) {
+ ModuleDef t = (ModuleDef)tree;
+ if ((t.mods == mods) &&
+ (t.name == name) &&
+ (t.tpe == tpe) &&
+ (t.impl == impl))
+ return t;
+ tree = make.ModuleDef(t.pos, mods, name, tpe, impl);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree ValDef(Tree tree,
+ int mods,
+ Name name,
+ Tree tpe,
+ Tree rhs) {
+ ValDef t = (ValDef)tree;
+ if ((t.mods == mods) &&
+ (t.name == name) &&
+ (t.tpe == tpe) &&
+ (t.rhs == rhs))
+ return t;
+ tree = make.ValDef(t.pos, mods, name, tpe, rhs);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree PatDef(Tree tree,
+ int mods,
+ Tree pat,
+ Tree rhs) {
+ PatDef t = (PatDef)tree;
+ if ((t.mods == mods) &&
+ (t.pat == pat) &&
+ (t.rhs == rhs))
+ return t;
+ tree = make.PatDef(t.pos, mods, pat, rhs);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree DefDef(Tree tree,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs) {
+ DefDef t = (DefDef)tree;
+ if ((t.mods == mods) &&
+ (t.name == name) &&
+ (t.tparams == tparams) &&
+ (t.vparams == vparams) &&
+ (t.tpe == tpe) &&
+ (t.rhs == rhs))
+ return t;
+ tree = make.DefDef(t.pos, mods, name, tparams, vparams, tpe, rhs);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree TypeDef(Tree tree,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ Tree rhs) {
+ TypeDef t = (TypeDef)tree;
+ if ((t.mods == mods) &&
+ (t.name == name) &&
+ (t.tparams == tparams) &&
+ (t.rhs == rhs))
+ return t;
+ tree = make.TypeDef(t.pos, mods, name, tparams, rhs);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Import(Tree tree,
+ Tree expr,
+ Name[] selectors) {
+ Import t = (Import)tree;
+ if (t.expr == expr && t.selectors == selectors)
+ return t;
+ tree = make.Import(t.pos, expr, selectors);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree CaseDef(Tree tree,
+ Tree pat,
+ Tree guard,
+ Tree body) {
+ CaseDef t = (CaseDef)tree;
+ if ((t.pat == pat) &&
+ (t.guard == guard) &&
+ (t.body == body))
+ return t;
+ tree = make.CaseDef(t.pos, pat, guard, body);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Template Template(Tree tree,
+ Tree[] parents,
+ Tree[] body) {
+ Template t = (Template)tree;
+ if ((t.parents == parents) &&
+ (t.body == body))
+ return t;
+ Template newTree = make.Template(t.pos, parents, body);
+ attribute(newTree, t);
+ return newTree;
+ }
+
+ public Tree LabelDef(Tree tree,
+ Tree[] params,
+ Tree rhs) {
+ LabelDef t = (LabelDef)tree;
+ if ((t.params == params) &&
+ (t.rhs == rhs))
+ return t;
+ tree = make.LabelDef(t.pos,params,rhs);
+ attribute(tree,t);
+ return tree;
+ }
+
+ public Tree Block(Tree tree,
+ Tree[] stats) {
+ Block t = (Block)tree;
+ if (t.stats == stats)
+ return t;
+ tree = make.Block(t.pos, stats);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Tuple(Tree tree,
+ Tree[] trees) {
+ Tuple t = (Tuple)tree;
+ if (t.trees == trees)
+ return t;
+ tree = make.Tuple(t.pos, trees);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Visitor(Tree tree,
+ CaseDef[] cases) {
+ Visitor t = (Visitor)tree;
+ if (t.cases == cases)
+ return t;
+ tree = make.Visitor(t.pos, cases);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Function(Tree tree,
+ ValDef[] vparams,
+ Tree body) {
+ Function t = (Function)tree;
+ if ((t.vparams == vparams) &&
+ (t.body == body))
+ return t;
+ tree = make.Function(t.pos, vparams, body);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Assign(Tree tree,
+ Tree lhs,
+ Tree rhs) {
+ Assign t = (Assign)tree;
+ if ((t.lhs == lhs) &&
+ (t.rhs == rhs))
+ return t;
+ tree = make.Assign(t.pos, lhs, rhs);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree If(Tree tree,
+ Tree cond,
+ Tree thenp,
+ Tree elsep) {
+ If t = (If)tree;
+ if ((t.cond == cond) &&
+ (t.thenp == thenp) &&
+ (t.elsep == elsep))
+ return t;
+ tree = make.If(t.pos, cond, thenp, elsep);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree New(Tree tree,
+ Template templ) {
+ New t = (New)tree;
+ if (t.templ == templ)
+ return t;
+ tree = make.New(t.pos, templ);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Typed(Tree tree,
+ Tree expr,
+ Tree tpe) {
+ Typed t = (Typed)tree;
+ if ((t.expr == expr) &&
+ (t.tpe == tpe))
+ return t;
+ tree = make.Typed(t.pos, expr, tpe);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree TypeApply(Tree tree,
+ Tree fun,
+ Tree[] args) {
+ TypeApply t = (TypeApply)tree;
+ if ((t.fun == fun) &&
+ (t.args == args))
+ return t;
+ tree = make.TypeApply(t.pos, fun, args);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Apply(Tree tree,
+ Tree fun,
+ Tree[] args) {
+ Apply t = (Apply)tree;
+ if ((t.fun == fun) &&
+ (t.args == args))
+ return t;
+ tree = make.Apply(t.pos, fun, args);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Super(Tree tree,
+ Tree tpe) {
+ Super t = (Super)tree;
+ if (t.tpe == tpe)
+ return t;
+ tree = make.Super(t.pos, tpe);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree This(Tree tree,
+ Tree qualifier) {
+ This t = (This)tree;
+ if (t.qualifier == qualifier)
+ return t;
+ tree = make.This(t.pos, qualifier);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Select(Tree tree,
+ Tree qualifier,
+ Name selector) {
+ Select t = (Select)tree;
+ if ((t.qualifier == qualifier) &&
+ (t.selector == selector))
+ return t;
+ tree = make.Select(t.pos, qualifier, selector);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Ident(Tree tree,
+ Name name) {
+ Ident t = (Ident)tree;
+ if (t.name == name)
+ return t;
+ tree = make.Ident(t.pos, name);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree Literal(Tree tree,
+ Object value) {
+ Literal t = (Literal)tree;
+ if (t.value == value)
+ return t;
+ tree = make.Literal(t.pos, value);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree SingletonType(Tree tree,
+ Tree ref) {
+ SingletonType t = (SingletonType)tree;
+ if (t.ref == ref)
+ return t;
+ tree = make.SingletonType(t.pos, ref);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree SelectFromType(Tree tree,
+ Tree qualifier,
+ Name selector) {
+ SelectFromType t = (SelectFromType)tree;
+ if (t.qualifier == qualifier &&
+ t.selector == selector)
+ return t;
+ tree = make.SelectFromType(t.pos, qualifier, selector);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree FunType(Tree tree,
+ Tree[] argtpes,
+ Tree restpe) {
+ FunType t = (FunType)tree;
+ if ((t.argtpes == argtpes) &&
+ (t.restpe == restpe))
+ return t;
+ tree = make.FunType(t.pos, argtpes, restpe);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree CompoundType(Tree tree,
+ Tree[] parents,
+ Tree[] refinements) {
+ CompoundType t = (CompoundType)tree;
+ if ((t.parents == parents) &&
+ (t.refinements == refinements))
+ return t;
+ tree = make.CompoundType(t.pos, parents, refinements);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree TupleType(Tree tree,
+ Tree[] types) {
+ TupleType t = (TupleType)tree;
+ if (t.types == types)
+ return t;
+ tree = make.TupleType(t.pos, types);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree AppliedType(Tree tree,
+ Tree tpe,
+ Tree[] args) {
+ AppliedType t = (AppliedType)tree;
+ if ((t.tpe == tpe) &&
+ (t.args == args))
+ return t;
+ tree = make.AppliedType(t.pos, tpe, args);
+ attribute(tree, t);
+ return tree;
+ }
+
+ public Tree CovariantType(Tree tree,
+ Tree tpe) {
+ CovariantType t = (CovariantType)tree;
+ if (t.tpe == tpe) return t;
+ tree = make.CovariantType(t.pos, tpe);
+ attribute(tree, t);
+ return tree;
+ }
+}
diff --git a/sources/scalac/ast/Transformer.java b/sources/scalac/ast/Transformer.java
new file mode 100644
index 0000000000..1789801c7e
--- /dev/null
+++ b/sources/scalac/ast/Transformer.java
@@ -0,0 +1,401 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast;
+
+import java.io.*;
+import java.util.*;
+import scalac.*;
+import scalac.util.*;
+import scalac.symtab.*;
+import Tree.*;
+
+
+/** A default transformer class. This class traverses the abstract
+ * syntax tree but does not do any transformations.
+ *
+ * @author Matthias Zenger
+ * @version 1.0
+ */
+public class Transformer extends Phase {
+ /** the tree factory
+ */
+ public final TreeFactory make;
+
+ /** a factory for copying trees; the attribution is preserved
+ * or translated according to the TreeCopyFactory; trees are
+ * only copied if new tree introduces changes
+ */
+ public final TreeCopyFactory copy;
+
+ /** a tree generator
+ */
+ public final TreeGen gen;
+
+ /** various constructors
+ */
+ public Transformer(Global global, PhaseDescriptor descr) {
+ this(global, descr, global.make, new LazyTreeFactory(global.make));
+ }
+
+ public Transformer(Global global,
+ PhaseDescriptor descr,
+ TreeFactory make) {
+ this(global, descr, make, new LazyTreeFactory(make));
+ }
+
+ public Transformer(Global global,
+ PhaseDescriptor descr,
+ TreeFactory make,
+ TreeCopyFactory copy) {
+ super(global, descr);
+ this.make = make;
+ this.copy = copy;
+ this.gen = global.treeGen;
+ }
+
+ public void apply() {
+ super.apply();
+ }
+
+ public void apply(Unit unit) {
+ unit.body = transform(unit.body);
+ }
+
+ public Tree[] transform(Tree[] ts) {
+ for (int i = 0; i < ts.length; i++) {
+ Tree t = transform(ts[i]);
+ if (t != ts[i]) {
+ Tree[] res = new Tree[ts.length];
+ System.arraycopy(ts, 0, res, 0, i);
+ res[i] = t;
+ for (int j = i + 1; j < ts.length; j++)
+ res[j] = transform(ts[j]);
+ return res;
+ }
+ }
+ return ts;
+ }
+
+ public Tree[][] transform(Tree[][] ts) {
+ for (int i = 0; i < ts.length; i++) {
+ Tree[] t = transform(ts[i]);
+ if (t != ts[i]) {
+ Tree[][] res = new Tree[ts.length][];
+ System.arraycopy(ts, 0, res, 0, i);
+ res[i] = t;
+ for (int j = i + 1; j < ts.length; j++)
+ res[j] = transform(ts[j]);
+ return res;
+ }
+ }
+ return ts;
+ }
+
+ public ValDef[][] transform(ValDef[][] ts) {
+ for (int i = 0; i < ts.length; i++) {
+ ValDef[] t = transform(ts[i]);
+ if (t != ts[i]) {
+ ValDef[][] res = new ValDef[ts.length][];
+ System.arraycopy(ts, 0, res, 0, i);
+ res[i] = t;
+ for (int j = i + 1; j < ts.length; j++)
+ res[j] = transform(ts[j]);
+ return res;
+ }
+ }
+ return ts;
+ }
+
+ public ValDef[] transform(ValDef[] ts) {
+ for (int i = 0; i < ts.length; i++) {
+ Tree t = transform(ts[i]);
+ if (t != ts[i]) {
+ ValDef[] res = new ValDef[ts.length];
+ System.arraycopy(ts, 0, res, 0, i);
+ res[i] = (ValDef)t;
+ for (int j = i + 1; j < ts.length; j++)
+ res[j] = (ValDef)transform(ts[j]);
+ return res;
+ }
+ }
+ return ts;
+ }
+
+ public TypeDef[] transform(TypeDef[] ts) {
+ for (int i = 0; i < ts.length; i++) {
+ Tree t = transform(ts[i]);
+ if (t != ts[i]) {
+ TypeDef[] res = new TypeDef[ts.length];
+ System.arraycopy(ts, 0, res, 0, i);
+ res[i] = (TypeDef)t;
+ for (int j = i + 1; j < ts.length; j++)
+ res[j] = (TypeDef)transform(ts[j]);
+ return res;
+ }
+ }
+ return ts;
+ }
+
+ public CaseDef[] transform(CaseDef[] ts) {
+ for (int i = 0; i < ts.length; i++) {
+ Tree t = transform(ts[i]);
+ if (t != ts[i]) {
+ CaseDef[] res = new CaseDef[ts.length];
+ System.arraycopy(ts, 0, res, 0, i);
+ res[i] = (CaseDef)t;
+ for (int j = i + 1; j < ts.length; j++)
+ res[j] = (CaseDef)transform(ts[j]);
+ return res;
+ }
+ }
+ return ts;
+ }
+
+ public Template transform(Template tree) {
+ return (Template)transform((Tree)tree);
+ }
+
+ public Tree transform(Tree tree) {
+ if (tree == null)
+ return null;
+ switch (tree) {
+ case ClassDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl):
+ return copy.ClassDef(tree,
+ mods,
+ name,
+ transform(tparams),
+ transform(vparams),
+ transform(tpe),
+ transform(impl));
+ case PackageDef(Tree packaged, Template impl):
+ return copy.PackageDef(tree,
+ transform(packaged),
+ transform(impl));
+ case ModuleDef(int mods,
+ Name name,
+ Tree tpe,
+ Template impl):
+ return copy.ModuleDef(tree,
+ mods,
+ name,
+ transform(tpe),
+ transform(impl));
+ case ValDef(int mods,
+ Name name,
+ Tree tpe,
+ Tree rhs):
+ return copy.ValDef(tree,
+ mods,
+ name,
+ transform(tpe),
+ transform(rhs));
+ case PatDef(int mods,
+ Tree pat,
+ Tree rhs):
+ return copy.PatDef(tree,
+ mods,
+ transform(pat),
+ transform(rhs));
+ case DefDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs):
+ return copy.DefDef(tree,
+ mods,
+ name,
+ transform(tparams),
+ transform(vparams),
+ transform(tpe),
+ transform(rhs));
+ case TypeDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ Tree rhs):
+ return copy.TypeDef(tree,
+ mods,
+ name,
+ transform(tparams),
+ transform(rhs));
+ case Import(Tree expr, Name[] selectors):
+ return copy.Import(tree,
+ transform(expr),
+ selectors);
+ case CaseDef(Tree pat, Tree guard, Tree body):
+ return copy.CaseDef(tree,
+ transform(pat),
+ transform(guard),
+ transform(body));
+ case Template(Tree[] parents, Tree[] body):
+ return copy.Template(tree,
+ transform(parents),
+ transform(body));
+ case LabelDef(Tree[] params,Tree rhs):
+ return copy.LabelDef(tree,
+ transform(params),
+ transform(rhs));
+ case Block(Tree[] stats):
+ return copy.Block(tree,
+ transform(stats));
+ case Tuple(Tree[] trees):
+ return copy.Tuple(tree,
+ transform(trees));
+ case Visitor(CaseDef[] cases):
+ return copy.Visitor(tree,
+ transform(cases));
+ case Function(ValDef[] vparams, Tree body):
+ return copy.Function(tree,
+ transform(vparams),
+ transform(body));
+ case Assign(Tree lhs, Tree rhs):
+ return copy.Assign(tree,
+ transform(lhs),
+ transform(rhs));
+ case If(Tree cond, Tree thenp, Tree elsep):
+ return copy.If(tree,
+ transform(cond),
+ transform(thenp),
+ transform(elsep));
+ case New(Template templ):
+ return copy.New(tree,
+ transform(templ));
+ case Typed(Tree expr, Tree tpe):
+ return copy.Typed(tree,
+ transform(expr),
+ transform(tpe));
+ case TypeApply(Tree fun, Tree[] args):
+ return copy.TypeApply(tree,
+ transform(fun),
+ transform(args));
+ case Apply(Tree fun, Tree[] args):
+ return copy.Apply(tree,
+ transform(fun),
+ transform(args));
+ case Super(Tree tpe):
+ return copy.Super(tree,
+ transform(tpe));
+ case This(Tree qualifier):
+ return copy.This(tree,
+ transform(qualifier));
+ case Select(Tree qualifier, Name selector):
+ return copy.Select(tree,
+ transform(qualifier),
+ selector);
+ case Ident(Name name):
+ return copy.Ident(tree, name);
+ case Literal(Object value):
+ return copy.Literal(tree, value);
+ case SingletonType(Tree ref):
+ return copy.SingletonType(tree,
+ transform(ref));
+ case SelectFromType(Tree qualifier, Name selector):
+ return copy.SelectFromType(tree,
+ transform(qualifier),
+ selector);
+ case FunType(Tree[] argtpes, Tree restpe):
+ return copy.FunType(tree,
+ transform(argtpes),
+ transform(restpe));
+ case CompoundType(Tree[] parents, Tree[] refinements):
+ return copy.CompoundType(tree,
+ transform(parents),
+ transform(refinements));
+ case TupleType(Tree[] types):
+ return copy.TupleType(tree,
+ transform(types));
+ case AppliedType(Tree tpe, Tree[] args):
+ return copy.AppliedType(tree,
+ transform(tpe),
+ transform(args));
+ case CovariantType(Tree tpe):
+ return copy.CovariantType(tree,
+ transform(tpe));
+ default:
+ return tree;
+ }
+ }
+ /* a full pattern-matching statement:
+
+ switch (tree) {
+ case PackageDef(Tree packaged, Template impl):
+
+ case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams,
+ Template impl):
+
+ case ModuleDef(int mods, Name name, Tree tpe, Template impl):
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+
+ case PatDef(int mods, Tree pat, Tree rhs):
+
+ case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams,
+ Tree tpe, Tree rhs):
+
+ case TypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs):
+
+ case Import(Tree expr):
+
+ case CaseDef(Tree pat, Tree guard, Tree body):
+
+ case Template(Tree[] baseClasses, Tree[] body):
+
+ case LabelDef(Tree[] params,Tree rhs):
+
+ case Block(Tree[] stats):
+
+ case Tuple(Tree[] trees):
+
+ case Visitor(CaseDef[] cases):
+
+ case Function(ValDef[] vparams, Tree body):
+
+ case Assign(Tree lhs, Tree rhs):
+
+ case If(Tree cond, Tree thenp, Tree elsep):
+
+ case New(Template templ):
+
+ case Typed(Tree expr, Tree tpe):
+
+ case TypeApply(Tree fun, Tree[] args):
+
+ case Apply(Tree fun, Tree[] args):
+
+ case Super(Tree tpe):
+
+ case Select(Tree qualifier, Name selector):
+
+ case Ident(Name name):
+
+ case Literal(int kind, Object value):
+
+ case SingletonType(Tree ref):
+
+ case SelectFromType(Tree qualifier, Name selector):
+
+ case FunType(Tree argtpe, Tree restpe):
+
+ case CompoundType(Tree[] baseTypes, Tree[] refinements):
+
+ case TupleType(Tree[] types):
+
+ case AppliedType(Tree tpe, Tree[] args):
+
+ default:
+ return tree;
+ }
+ */
+
+}
diff --git a/sources/scalac/ast/Traverser.java b/sources/scalac/ast/Traverser.java
new file mode 100644
index 0000000000..5c39d9e315
--- /dev/null
+++ b/sources/scalac/ast/Traverser.java
@@ -0,0 +1,215 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+\* */
+
+// $Id$
+
+package scalac.ast;
+
+import java.io.*;
+import java.util.*;
+import scalac.*;
+import scalac.util.*;
+import Tree.*;
+
+
+/** Class to traverse a tree without modifying it.
+ *
+ * @author Michel Schinz
+ */
+
+public class Traverser {
+ public Traverser() {}
+
+ // this should be removed in the future
+ public Traverser(Global global) {}
+
+ public void traverse(Unit unit) {
+ traverse(unit.body);
+ }
+
+ public void traverse(Tree tree) {
+ switch (tree) {
+ case Bad():
+ case Empty:
+ case Ident(_):
+ case Literal(_):
+ return;
+
+ case ClassDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl):
+ traverse(tparams);
+ traverse(vparams);
+ traverse(tpe);
+ traverse(impl);
+ return;
+
+ case PackageDef(Tree packaged, Template impl) :
+ traverse(packaged);
+ traverse(impl);
+ return;
+
+ case ModuleDef(int mods, Name name, Tree tpe, Template impl):
+ traverse(tpe);
+ traverse(impl);
+ return;
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ traverse(tpe);
+ traverse(rhs);
+ return;
+
+ case PatDef(int mods, Tree pat, Tree rhs):
+ traverse(pat);
+ traverse(rhs);
+ return;
+
+ case DefDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs):
+ traverse(tparams);
+ traverse(vparams);
+ traverse(tpe);
+ traverse(rhs);
+ return;
+
+ case TypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs):
+ traverse(tparams);
+ traverse(rhs);
+ return;
+
+ case Import(Tree expr, Name[] selectors):
+ traverse(expr);
+ return;
+
+ case CaseDef(Tree pat, Tree guard, Tree body):
+ traverse(pat);
+ traverse(guard);
+ traverse(body);
+ return;
+
+ case Template(Tree[] baseClasses, Tree[] body):
+ traverse(baseClasses);
+ traverse(body);
+ return;
+
+ case LabelDef(Tree[] params,Tree rhs):
+ traverse(params);
+ traverse(rhs);
+ return;
+
+ case Block(Tree[] stats):
+ traverse(stats);
+ return;
+
+ case Tuple(Tree[] trees):
+ traverse(trees);
+ return;
+
+ case Visitor(CaseDef[] cases):
+ traverse(cases);
+ return;
+
+ case Function(ValDef[] vparams, Tree body):
+ traverse(vparams);
+ traverse(body);
+ return;
+
+ case Assign(Tree lhs, Tree rhs):
+ traverse(lhs);
+ traverse(rhs);
+ return;
+
+ case If(Tree cond, Tree thenp, Tree elsep):
+ traverse(cond);
+ traverse(thenp);
+ traverse(elsep);
+ return;
+
+ case New(Template templ):
+ traverse(templ);
+ return;
+
+ case Typed(Tree expr, Tree tpe):
+ traverse(expr);
+ traverse(tpe);
+ return;
+
+ case TypeApply(Tree fun, Tree[] tparams):
+ traverse(fun);
+ traverse(tparams);
+ return;
+
+ case Apply(Tree fun, Tree[] vparam):
+ traverse(fun);
+ traverse(vparam);
+ return;
+
+ case Super(Tree tpe):
+ traverse(tpe);
+ return;
+
+ case This(Tree qualifier):
+ traverse(qualifier);
+ return;
+
+ case Select(Tree qualifier, Name selector):
+ traverse(qualifier);
+ return;
+
+ case SingletonType(Tree ref):
+ traverse(ref);
+ return;
+
+ case SelectFromType(Tree qualifier, Name selector):
+ traverse(qualifier);
+ return;
+
+ case FunType(Tree[] argtpes, Tree restpe):
+ traverse(argtpes);
+ traverse(restpe);
+ return;
+
+ case CompoundType(Tree[] baseTypes, Tree[] refinements):
+ traverse(baseTypes);
+ traverse(refinements);
+ return;
+
+ case TupleType(Tree[] types):
+ traverse(types);
+ return;
+
+ case AppliedType(Tree tpe, Tree[] args):
+ traverse(tpe);
+ traverse(args);
+ return;
+
+ case CovariantType(Tree tpe):
+ traverse(tpe);
+ return;
+
+ default:
+ throw new ApplicationError("unknown node " + tree);
+ }
+ }
+
+ public void traverse(Tree[] array) {
+ for (int i = 0; i < array.length; ++i)
+ traverse(array[i]);
+ }
+
+ public void traverse(Tree[][] array) {
+ for (int i = 0; i < array.length; ++i)
+ traverse(array[i]);
+ }
+}
diff --git a/sources/scalac/ast/Tree.java b/sources/scalac/ast/Tree.java
new file mode 100644
index 0000000000..80304ea78e
--- /dev/null
+++ b/sources/scalac/ast/Tree.java
@@ -0,0 +1,771 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast;
+
+import scalac.ast.printer.*;
+import scalac.ApplicationError;
+import scalac.util.Name;
+import scalac.util.Position;
+import scalac.symtab.Type;
+import scalac.symtab.Symbol;
+
+public class Tree {
+
+ public int pos = Position.NOPOS;
+ public Type type;
+
+/** empty tree array
+ */
+ public static final Tree[] EMPTY_ARRAY = new Tree[0];
+
+/** representation for parser errors
+ */
+ public case Bad();
+
+/** a tree node for the absence of a tree
+ */
+ public case Empty;
+ static { Empty.type = Type.NoType; }
+
+/** class and data declaration
+ */
+ public case ClassDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl) {
+ assert name.isTypeName();
+ }
+
+/** package declaration
+ */
+ public case PackageDef(Tree packaged,
+ Template impl) {
+ if (!packaged.isTerm())
+ throw new ApplicationError("PackageDef expects term as rhs.");
+ }
+
+/** module declaration
+ */
+ public case ModuleDef(int mods,
+ Name name,
+ Tree tpe,
+ Template impl) {
+ assert !name.isTypeName();
+ }
+
+/** var or let declaration
+ */
+ public case ValDef(int mods,
+ Name name,
+ Tree tpe,
+ Tree rhs) {
+ assert !name.isTypeName();
+ if (!tpe.isType())
+ throw new ApplicationError("ValDef expects type as tpe; found: " + tpe);
+ if (!rhs.isTerm())
+ throw new ApplicationError("ValDef expects term as rhs.");
+ }
+
+/** val declaration
+ */
+ public case PatDef(int mods,
+ Tree pat,
+ Tree rhs) {
+ if (!rhs.isTerm())
+ throw new ApplicationError("PatDef expects term as rhs.");
+ }
+
+/** def declaration
+ */
+ public case DefDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs) {
+ assert !name.isTypeName();
+ if (!tpe.isType())
+ throw new ApplicationError("DefDef expects type as tpe.");
+ if (!rhs.isTerm())
+ throw new ApplicationError("DefDef expects term as rhs. Found: " + rhs.getClass());
+ }
+
+/** type declaration
+ */
+ public case TypeDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ Tree rhs) {
+ assert name.isTypeName();
+ if (!rhs.isType())
+ throw new ApplicationError("TypeDef expects type as rhs; found: " + rhs);
+ }
+
+/** import declaration
+ */
+ public case Import(Tree expr, Name[] selectors) {
+ if (!expr.isTerm())
+ throw new ApplicationError("Import expects term.");
+ }
+
+/** case declaration
+ */
+ public case CaseDef(Tree pat,
+ Tree guard,
+ Tree body) {
+ if (!guard.isTerm())
+ throw new ApplicationError("CaseDef expects term as guard.");
+ if (!body.isTerm())
+ throw new ApplicationError("CaseDef expects term as body.");
+ }
+
+/** instantiation templates
+*/
+ public case Template(Tree[] parents,
+ Tree[] body) {
+ if (parents != null) {
+ for (int i = 0; i < parents.length; i++) {
+ if (!parents[i].isTerm())
+ throw new ApplicationError("Template requires terms as baseClasses.");
+ }
+ }
+ }
+
+/** labelled expression - the symbols in the array (must be Idents!) are those the
+ label takes as argument
+*/
+ public case LabelDef(Tree[] params,Tree rhs) {
+ for (int i = 0;i < params.length; i++) {
+ if (!(params[i] instanceof Ident))
+ throw new ApplicationError("LabelDef requires Idents");
+ }
+ }
+
+/** block of expressions (semicolon separated expressions)
+ */
+ public case Block(Tree[] stats);
+
+/** tuple of expressions (comma separated expressions)
+ */
+ public case Tuple(Tree[] trees) {
+ if (trees != null) {
+ for (int i = 0; i < trees.length; i++) {
+ if (!trees[i].isTerm())
+ throw new ApplicationError("Tuple requires terms");
+ }
+ }
+ }
+
+/** visitor (a sequence of cases)
+ */
+ public case Visitor(CaseDef[] cases);
+
+/** an anonymous function
+ */
+ public case Function(ValDef[] vparams,
+ Tree body) {
+ if (!body.isTerm())
+ throw new ApplicationError("Function body has to be a term.");
+ }
+
+/** assignment
+ */
+ public case Assign(Tree lhs,
+ Tree rhs) {
+ if (!lhs.isTerm())
+ throw new ApplicationError("lhs of Assign has to be a term.");
+ if (!rhs.isTerm())
+ throw new ApplicationError("rhs of Assign has to be a term.");
+ }
+
+/** conditional expression
+ */
+ public case If(Tree cond,
+ Tree thenp,
+ Tree elsep) {
+ assert cond.isTerm() &&
+ thenp.isTerm() &&
+ elsep.isTerm();
+ }
+
+/** instantiation
+ */
+ public case New(Template templ);
+
+/** type annotation
+ */
+ public case Typed(Tree expr,
+ Tree tpe) {
+ if (!expr.isTerm())
+ throw new ApplicationError("Typed expects term as first argument.");
+ if (!tpe.isType())
+ throw new ApplicationError("Typed expects type as second argument.");
+ }
+
+/** type application
+ */
+ public case TypeApply(Tree fun,
+ Tree[] args) {
+ if (!fun.isTerm()) {
+ new TextTreePrinter().print(fun).println().end();//debug
+ throw new ApplicationError("TypeApply expects term as function.");
+ }
+ for (int i = 0; i < args.length; i++) {
+ if (!args[i].isType())
+ throw new ApplicationError("TypeApply expects types as arguments.");
+ }
+ }
+
+/** value application
+ */
+ public case Apply(Tree fun,
+ Tree[] args) {
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ if (!args[i].isTerm())
+ throw new ApplicationError("Apply expects terms as arguments. Found: " + args[i].getClass());
+ }
+ }
+ }
+
+/** super reference
+ */
+ public case Super(Tree tpe) {
+ if (!tpe.isType()) {
+ throw new ApplicationError("Super expects type.");
+ }
+ }
+
+/** self reference
+ */
+ public case This(Tree qualifier) {
+ if (!qualifier.isType())
+ throw new ApplicationError("This expects type.");
+ }
+
+/** designator
+ */
+ public case Select(Tree qualifier,
+ Name selector) {
+ if (!qualifier.isTerm())
+ throw new ApplicationError("Select expects term.");
+ }
+
+/** identifier
+ */
+ public case Ident(Name name) {
+ assert name != null;
+ }
+
+/** literal
+ */
+ public case Literal(Object value);
+
+/** singleton type
+ */
+ public case SingletonType(Tree ref) {
+ if (!ref.isTerm())
+ throw new ApplicationError("SingletonType expects term.");
+ }
+
+/** type selection
+ */
+ public case SelectFromType(Tree qualifier,
+ Name selector) {
+ if (!qualifier.isType())
+ throw new ApplicationError("SelectFromType expects type.");
+ assert selector.isTypeName();
+ }
+
+/** function type
+ */
+ public case FunType(Tree[] argtpes,
+ Tree restpe) {
+ for (int i = 0; i < argtpes.length; i++)
+ if (!argtpes[i].isType())
+ throw new ApplicationError("FunType requires types as args.");
+ if (!restpe.isType())
+ throw new ApplicationError("FunType requires type as result.");
+ }
+
+/** object type (~ Template)
+ */
+ public case CompoundType(Tree[] parents,
+ Tree[] refinements) {
+ if (parents != null) {
+ assert parents.length > 0;
+ for (int i = 0; i < parents.length; i++) {
+ if (!parents[i].isType())
+ throw new ApplicationError("CompoundType requires types as parents.");
+ }
+ }
+ }
+
+/** tuple type (~ Tuple)
+ */
+ public case TupleType(Tree[] types) {
+ if (types != null) {
+ for (int i = 0; i < types.length; i++) {
+ if (!types[i].isType())
+ throw new ApplicationError("TupleType requires types.");
+ }
+ }
+ }
+
+ /** applied type
+ */
+ public case AppliedType(Tree tpe, Tree[] args) {
+ assert tpe.isType() : this;
+ for (int i = 0; i < args.length; i++) assert args[i].isType() : args[i];
+ }
+
+ /** a covariant type
+ */
+ public case CovariantType(Tree tpe) {
+ assert tpe.isType();
+ }
+
+ /** Get the type of the node. */
+ public Type type() {
+ assert type != null : this;
+ return type;
+ }
+
+ /** Set the type of the node. */
+ public Tree setType(Type type) {
+ assert !(type instanceof Type.LazyType) : symbol();
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Get types attached to array of nodes.
+ */
+ public static Type[] typeOf(Tree[] trees) {
+ Type[] tps = new Type[trees.length];
+ for (int i = 0; i < trees.length; i++)
+ tps[i] = trees[i].type();
+ return tps;
+ }
+
+ /** Has this tree a symbol field? */
+ public boolean hasSymbol() {
+ return false;
+ }
+
+ /**
+ * Get symbol attached to the node, if any.
+ */
+ public Symbol symbol () {
+ return null;
+ }
+
+ /**
+ * Set symbol attached to the node, if possible.
+ */
+ public Tree setSymbol(Symbol sym) {
+ throw new ApplicationError ("no settable symbol for node", this);
+ }
+
+ /**
+ * Get symbols attached to array of nodes.
+ */
+ public static Symbol[] symbolOf(Tree[] trees) {
+ Symbol[] syms = new Symbol[trees.length];
+ for (int i = 0; i < trees.length; i++)
+ syms[i] = trees[i].symbol();
+ return syms;
+ }
+
+ /**
+ * Tells if the tree defines a symbol.
+ **/
+ public boolean definesSymbol() {
+ return false;
+ }
+
+ /** Get string corresponding to this tree
+ * only implemented for prefix trees, maybe we should generalize this;
+ * the PatternMatch phase needs support for Apply, so this case got added
+ */
+ public String toString() {
+ switch (this) {
+ case This(Tree qual):
+ return (qual == Tree.Empty) ? "this" : qual + ".this";
+ case Select(Tree qual, Name name):
+ return qual + "." + name;
+ case Ident(Name name):
+ return name.toString();
+ case Apply(Tree fn, Tree[] args):
+ String res = fn + "(";
+ if ((args == null) || (args.length == 0))
+ return res + ")";
+ res += args[0].toString();
+ for (int i = 1; i < args.length; i++)
+ res += ", " + args[i];
+ return res + ")";
+ case Literal(Object value):
+ if (value instanceof String)
+ return "\"" + value + "\"";
+ else
+ return value.toString();
+ case Import(Tree expr, _):
+ return "import " + expr;
+ case Empty:
+ return "<empty>";
+ default:
+ return super.toString();
+ }
+ }
+
+ public static class ExtBad extends Bad {
+ private Symbol symbol;
+
+ public ExtBad() {
+ super();
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+ }
+
+ public static class ExtClassDef extends ClassDef {
+ private Symbol symbol;
+
+ public ExtClassDef(int mods, Name name, TypeDef[] tparams,
+ ValDef[][] vparams, Tree tpe, Template impl)
+ {
+ super(mods, name, tparams, vparams, tpe, impl);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+
+ public boolean definesSymbol() {
+ return true;
+ }
+ }
+
+ public static class ExtModuleDef extends ModuleDef {
+ private Symbol symbol;
+
+ public ExtModuleDef(int mods, Name name, Tree tpe, Template impl)
+ {
+ super(mods, name, tpe, impl);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+
+ public boolean definesSymbol() {
+ return true;
+ }
+ }
+
+ public static class ExtValDef extends ValDef {
+
+ public static final ValDef[] EMPTY_ARRAY = new ValDef[0];
+ public static final ValDef[][] EMPTY_ARRAY_ARRAY = new ValDef[0][0];
+
+ private Symbol symbol;
+
+ public ExtValDef(int mods, Name name, Tree tpe, Tree rhs)
+ {
+ super(mods, name, tpe, rhs);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+
+ public boolean definesSymbol() {
+ return true;
+ }
+ }
+
+ public static class ExtDefDef extends DefDef {
+ private Symbol symbol;
+
+ public ExtDefDef(int mods, Name name, TypeDef[] tparams,
+ ValDef[][] vparams, Tree tpe, Tree rhs)
+ {
+ super(mods, name, tparams, vparams, tpe, rhs);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+
+ public boolean definesSymbol() {
+ return true;
+ }
+ }
+
+ public static class ExtTypeDef extends TypeDef {
+ private Symbol symbol;
+
+ public static final TypeDef[] EMPTY_ARRAY = new TypeDef[0];
+
+ public ExtTypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs)
+ {
+ super(mods, name, tparams, rhs);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+
+ public boolean definesSymbol() {
+ return true;
+ }
+ }
+
+ public static class ExtImport extends Import {
+ private Symbol symbol;
+
+ public ExtImport(Tree expr, Name[] selectors) {
+ super(expr, selectors);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+ }
+
+ public static class ExtLabelDef extends LabelDef {
+ private Symbol symbol;
+
+ public ExtLabelDef(Tree[] params,Tree rhs) {
+ super(params,rhs);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+
+ public boolean definesSymbol() {
+ return true;
+ }
+ }
+
+ public static class ExtSelect extends Select {
+ private Symbol symbol;
+
+ public ExtSelect(Tree qualifier, Name selector) {
+ super(qualifier, selector);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+ }
+
+ public static class ExtSelectFromType extends SelectFromType {
+ private Symbol symbol;
+
+ public ExtSelectFromType(Tree qualifier, Name selector) {
+ super(qualifier, selector);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+ }
+
+ public static class ExtIdent extends Ident {
+ private Symbol symbol;
+
+ public ExtIdent(Name name) {
+ super(name);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+ }
+
+ public static class ExtTemplate extends Template {
+ private Symbol symbol;
+
+ public ExtTemplate(Tree[] parents, Tree[] body) {
+ super(parents, body);
+ }
+
+ public boolean hasSymbol() {
+ return true;
+ }
+
+ public Symbol symbol() {
+ return symbol;
+ }
+
+ public Tree setSymbol(Symbol symbol) {
+ this.symbol = symbol;
+ return this;
+ }
+
+ public boolean definesSymbol() {
+ return true;
+ }
+ }
+
+ public boolean isTerm() {
+ switch(this) {
+ case Bad():
+ case Empty:
+ case Tuple(_):
+ case If(_, _, _):
+ case Typed(_, _):
+ case Apply(_, _):
+ case TypeApply(_, _):
+ case Visitor(_):
+ case New(_):
+ case Literal(_):
+ case LabelDef(_,_):
+ case Block(_):
+ case Function(_, _):
+ case Assign(_, _):
+ case Super(_):
+ case This(_):
+ return true;
+ case Ident(Name name):
+ return !name.isTypeName();
+ case Select(_, Name name):
+ return !name.isTypeName();
+ default:
+ return false;
+ }
+ }
+
+ public boolean isType() {
+ switch(this) {
+ case Bad():
+ case Empty:
+ case SingletonType(_):
+ case SelectFromType(_, _):
+ case CompoundType(_, _):
+ case FunType(_, _):
+ case TupleType(_):
+ case AppliedType(_, _):
+ case CovariantType(_):
+ return true;
+ case Ident(Name name):
+ return name.isTypeName();
+ case Select(_, Name name):
+ return name.isTypeName();
+ default:
+ return false;
+ }
+ }
+}
+
diff --git a/sources/scalac/ast/TreeCopyFactory.java b/sources/scalac/ast/TreeCopyFactory.java
new file mode 100644
index 0000000000..5a9074c39f
--- /dev/null
+++ b/sources/scalac/ast/TreeCopyFactory.java
@@ -0,0 +1,165 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast;
+
+import scalac.util.Name;
+import Tree.*;
+
+/**
+ * Interface for a kind of factory which, for each node constructor,
+ * takes an original node from which some data will be copied or shared.
+ *
+ * @author Michel Schinz
+ * @version 1.1
+ */
+public interface TreeCopyFactory {
+
+ public void attribute(Tree newTree, Tree oldTree);
+
+ public Tree Bad(Tree tree);
+
+ public Tree ClassDef(Tree tree,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl);
+
+ public Tree PackageDef(Tree tree,
+ Tree packaged,
+ Template impl);
+
+ public Tree ModuleDef(Tree tree,
+ int mods,
+ Name name,
+ Tree tpe,
+ Template impl);
+
+ public Tree ValDef(Tree tree,
+ int mods,
+ Name name,
+ Tree tpe,
+ Tree rhs);
+
+ public Tree PatDef(Tree tree,
+ int mods,
+ Tree pat,
+ Tree rhs);
+
+ public Tree DefDef(Tree tree,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs);
+
+ public Tree TypeDef(Tree tree,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ Tree rhs);
+
+ public Tree Import(Tree tree,
+ Tree expr,
+ Name[] selectors);
+
+ public Tree CaseDef(Tree tree,
+ Tree pat,
+ Tree guard,
+ Tree body);
+
+ public Template Template(Tree tree,
+ Tree[] baseClasses,
+ Tree[] body);
+
+ public Tree LabelDef(Tree tree,
+ Tree[] params,
+ Tree rhs);
+
+ public Tree Block(Tree tree,
+ Tree[] stats);
+
+ public Tree Tuple(Tree tree,
+ Tree[] trees);
+
+ public Tree Visitor(Tree tree,
+ CaseDef[] cases);
+
+ public Tree Function(Tree tree,
+ ValDef[] vparams,
+ Tree body);
+
+ public Tree Assign(Tree tree,
+ Tree lhs,
+ Tree rhs);
+
+ public Tree If(Tree tree,
+ Tree cond,
+ Tree thenp,
+ Tree elsep);
+
+ public Tree New(Tree tree,
+ Template templ);
+
+ public Tree Typed(Tree tree,
+ Tree expr,
+ Tree tpe);
+
+ public Tree TypeApply(Tree tree,
+ Tree fun,
+ Tree[] args);
+
+ public Tree Apply(Tree tree,
+ Tree fun,
+ Tree[] args);
+
+ public Tree Super(Tree tree,
+ Tree tpe);
+
+ public Tree This(Tree tree,
+ Tree qualifier);
+
+ public Tree Select(Tree tree,
+ Tree qualifier,
+ Name selector);
+
+ public Tree Ident(Tree tree,
+ Name name);
+
+ public Tree Literal(Tree tree,
+ Object value);
+
+ public Tree SingletonType(Tree tree,
+ Tree ref);
+
+ public Tree SelectFromType(Tree tree,
+ Tree qualifier,
+ Name selector);
+
+ public Tree FunType(Tree tree,
+ Tree[] argtpes,
+ Tree restpe);
+
+ public Tree CompoundType(Tree tree,
+ Tree[] baseTypes,
+ Tree[] refinements);
+
+ public Tree TupleType(Tree tree,
+ Tree[] types);
+
+ public Tree AppliedType(Tree tree,
+ Tree tpe,
+ Tree[] args);
+
+ public Tree CovariantType(Tree tree,
+ Tree tpe);
+
+}
diff --git a/sources/scalac/ast/TreeCreator.java b/sources/scalac/ast/TreeCreator.java
new file mode 100644
index 0000000000..6cb8afa1d8
--- /dev/null
+++ b/sources/scalac/ast/TreeCreator.java
@@ -0,0 +1,293 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast;
+
+import Tree.*;
+import scalac.util.Name;
+
+
+public class TreeCreator implements TreeFactory {
+
+ public Tree Bad(int pos) {
+ Tree t = new ExtBad();
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree ClassDef(int pos,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl) {
+ Tree t = new ExtClassDef(mods, name, tparams, vparams, tpe, impl);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree PackageDef(int pos,
+ Tree packaged,
+ Template impl) {
+ Tree t = new PackageDef(packaged, impl);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree ModuleDef(int pos,
+ int mods,
+ Name name,
+ Tree tpe,
+ Template body) {
+ Tree t = new ExtModuleDef(mods, name, tpe, body);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree ValDef(int pos,
+ int mods,
+ Name name,
+ Tree tpe,
+ Tree rhs) {
+ Tree t = new ExtValDef(mods, name, tpe, rhs);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree PatDef(int pos,
+ int mods,
+ Tree pat,
+ Tree rhs) {
+ Tree t = new PatDef(mods, pat, rhs);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree DefDef(int pos,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs) {
+ Tree t = new ExtDefDef(mods, name, tparams, vparams, tpe, rhs);
+ t.pos = pos;
+ return t;
+ }
+
+
+ public Tree TypeDef(int pos,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ Tree rhs) {
+ Tree t = new ExtTypeDef(mods, name, tparams, rhs);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Import(int pos,
+ Tree expr,
+ Name[] selectors) {
+ Tree t = new ExtImport(expr, selectors);
+ t.pos = pos;
+ return t;
+ }
+
+ public CaseDef CaseDef(int pos,
+ Tree pat,
+ Tree guard,
+ Tree body) {
+ CaseDef t = new CaseDef(pat, guard, body);
+ t.pos = pos;
+ return t;
+ }
+
+ public Template Template(int pos,
+ Tree[] baseClasses,
+ Tree[] body) {
+ Template t = new ExtTemplate(baseClasses, body);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree LabelDef(int pos,
+ Tree[] params,
+ Tree body) {
+ Tree t = new ExtLabelDef(params,body);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Block(int pos,
+ Tree[] stats) {
+ Tree t = new Block(stats);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Tuple(int pos,
+ Tree[] trees) {
+ Tree t = new Tuple(trees);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Visitor(int pos,
+ CaseDef[] cases) {
+ Tree t = new Visitor(cases);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Function(int pos,
+ ValDef[] vparams,
+ Tree body) {
+ Tree t = new Function(vparams, body);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Assign(int pos,
+ Tree lhs,
+ Tree rhs) {
+ Tree t = new Assign(lhs, rhs);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree If(int pos,
+ Tree cond,
+ Tree thenp,
+ Tree elsep) {
+ Tree t = new If(cond, thenp, elsep);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree New(int pos,
+ Template templ) {
+ Tree t = new New(templ);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Typed(int pos,
+ Tree expr,
+ Tree tpe) {
+ Tree t = new Typed(expr, tpe);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree TypeApply(int pos,
+ Tree fun,
+ Tree[] tparams) {
+ Tree t = new TypeApply(fun, tparams);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Apply(int pos,
+ Tree fun,
+ Tree[] vparam) {
+ Tree t = new Apply(fun, vparam);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Super(int pos,
+ Tree tpe) {
+ Tree t = new Super(tpe);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree This(int pos,
+ Tree qualifier) {
+ Tree t = new This(qualifier);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Select(int pos,
+ Tree qualifier,
+ Name selector) {
+ Tree t = new ExtSelect(qualifier, selector);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Ident(int pos,
+ Name name) {
+ Tree t = new ExtIdent(name);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree Literal(int pos,
+ Object value) {
+ Tree t = new Literal(value);
+ t.pos = pos;
+ return t;
+ }
+
+
+ public Tree SingletonType(int pos, Tree ref) {
+ Tree t = new SingletonType(ref);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree SelectFromType(int pos,
+ Tree qualifier,
+ Name selector) {
+ Tree t = new ExtSelectFromType(qualifier, selector);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree FunType(int pos,
+ Tree[] argtpes,
+ Tree restpe) {
+ Tree t = new FunType(argtpes, restpe);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree CompoundType(int pos,
+ Tree[] mixins,
+ Tree[] fields) {
+ Tree t = new CompoundType(mixins, fields);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree TupleType(int pos,
+ Tree[] types) {
+ Tree t = new TupleType(types);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree AppliedType(int pos,
+ Tree tpe,
+ Tree[] args) {
+ Tree t = new AppliedType(tpe, args);
+ t.pos = pos;
+ return t;
+ }
+
+ public Tree CovariantType(int pos,
+ Tree tpe) {
+ Tree t = new CovariantType(tpe);
+ t.pos = pos;
+ return t;
+ }
+}
diff --git a/sources/scalac/ast/TreeFactory.java b/sources/scalac/ast/TreeFactory.java
new file mode 100644
index 0000000000..31ee67042e
--- /dev/null
+++ b/sources/scalac/ast/TreeFactory.java
@@ -0,0 +1,155 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast;
+
+import scalac.util.Name;
+import Tree.*;
+
+public interface TreeFactory {
+
+ public Tree Bad(int pos);
+
+ public Tree ClassDef(int pos,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl);
+
+ public Tree PackageDef(int pos,
+ Tree packaged,
+ Template impl);
+
+ public Tree ModuleDef(int pos,
+ int mods,
+ Name name,
+ Tree tpe,
+ Template impl);
+
+ public Tree ValDef(int pos,
+ int mods,
+ Name name,
+ Tree tpe,
+ Tree rhs);
+
+ public Tree PatDef(int pos,
+ int mods,
+ Tree pat,
+ Tree rhs);
+
+ public Tree DefDef(int pos,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs);
+
+ public Tree TypeDef(int pos,
+ int mods,
+ Name name,
+ TypeDef[] tparams,
+ Tree rhs);
+
+ public Tree Import(int pos,
+ Tree expr,
+ Name[] selectors);
+
+ public CaseDef CaseDef(int pos,
+ Tree pat,
+ Tree guard,
+ Tree body);
+
+ public Template Template(int pos,
+ Tree[] baseClasses,
+ Tree[] body);
+
+ public Tree LabelDef(int pos,
+ Tree[] params,
+ Tree rhs);
+
+ public Tree Block(int pos,
+ Tree[] stats);
+
+ public Tree Tuple(int pos,
+ Tree[] trees);
+
+ public Tree Visitor(int pos,
+ CaseDef[] cases);
+
+ public Tree Function(int pos,
+ ValDef[] vparams,
+ Tree body);
+
+ public Tree Assign(int pos,
+ Tree lhs,
+ Tree rhs);
+
+ public Tree If(int pos,
+ Tree cond,
+ Tree thenp,
+ Tree elsep);
+
+ public Tree New(int pos,
+ Template templ);
+
+ public Tree Typed(int pos,
+ Tree expr,
+ Tree tpe);
+
+ public Tree TypeApply(int pos,
+ Tree fun,
+ Tree[] tparams);
+
+ public Tree Apply(int pos,
+ Tree fun,
+ Tree[] vparam);
+
+ public Tree Super(int pos,
+ Tree tpe);
+
+ public Tree This(int pos,
+ Tree qualifier);
+
+ public Tree Select(int pos,
+ Tree qualifier,
+ Name selector);
+
+ public Tree Ident(int pos,
+ Name name);
+
+ public Tree Literal(int pos,
+ Object value);
+
+ public Tree SingletonType(int pos,
+ Tree ref);
+
+ public Tree SelectFromType(int pos,
+ Tree qualifier,
+ Name selector);
+
+ public Tree FunType(int pos,
+ Tree[] argtpes,
+ Tree restpe);
+
+ public Tree CompoundType(int pos,
+ Tree[] baseTypes,
+ Tree[] refinements);
+
+ public Tree TupleType(int pos,
+ Tree[] types);
+
+ public Tree AppliedType(int pos,
+ Tree tpe,
+ Tree[] args);
+
+ public Tree CovariantType(int pos,
+ Tree tpe);
+}
diff --git a/sources/scalac/ast/TreeGen.java b/sources/scalac/ast/TreeGen.java
new file mode 100644
index 0000000000..26b02eb862
--- /dev/null
+++ b/sources/scalac/ast/TreeGen.java
@@ -0,0 +1,562 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast;
+
+import java.io.*;
+import java.util.*;
+import scalac.*;
+import scalac.symtab.*;
+import scalac.util.*;
+import Tree.*;
+
+/** A helper class for building trees
+ *
+ * @author Martin Odersky, Christine Roeckl
+ * @version 1.0
+ */
+public class TreeGen implements Kinds, Modifiers {
+
+ /********************************************************************************/
+ /********************************************************************************/
+ /** VARIABLES **/
+
+ /** the global environment
+ */
+ protected Global global;
+
+ /** the global definitions
+ */
+ protected Definitions definitions;
+
+ /** the tree factory
+ */
+ public TreeFactory make;
+
+ /************************************************************************/
+ /************************************************************************/
+ /** CONSTRUCTORS **/
+
+ public TreeGen(Global global, TreeFactory make) {
+ this.global = global;
+ this.definitions = global.definitions;
+ this.make = make;
+ }
+
+ public TreeGen(Global global) {
+ this(global, global.make);
+ }
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** METHODS **/
+
+ /** Create a dummy symbol to be used for templates.
+ */
+ public Symbol localDummy(int pos, Symbol owner) {
+ return new TermSymbol(pos, Name.EMPTY, owner, 0)
+ .setInfo(Type.NoType);
+ }
+
+ public Tree mkRef(int pos, Type pre, Symbol sym) {
+ if (pre == Type.localThisType || pre.symbol().isRoot())
+ return Ident(pos, sym);
+ else
+ return Select(pos, mkStableId(pos, pre), sym);
+ }
+
+ /** Build and attribute stable identifier tree corresponding to given prefix.
+ */
+ public Tree mkStableId(int pos, Type pre) {
+ switch (pre.expandModuleThis()) {
+ case ThisType(Symbol sym):
+ return make.This(pos, Ident(pos, sym)).setType(pre);
+ case SingleType(Type pre1, Symbol sym):
+ return mkRef(pos, pre1, sym);
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** Build and attribute tree corresponding to given type.
+ */
+ public Tree mkType(int pos, Type type) {
+ Tree tree = mkTycon(pos, type);
+ switch (type) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (args.length != 0)
+ return make.AppliedType(pos, tree, mkType(pos, args))
+ .setType(type);
+ }
+ return tree;
+ }
+
+ /** Build and attribute tree corresponding to given type constructor.
+ */
+ public Tree mkTycon(int pos, Type type) {
+ //System.out.println("making type " + type);//DEBUG
+ switch (type) {
+
+ case NoType:
+ return Tree.Empty;
+
+ case ErrorType:
+ case AnyType:
+ return make.Bad(pos).setSymbol(Symbol.ERROR).setType(type);
+
+ case ThisType(_):
+ case SingleType(_, _):
+ return make.SingletonType(pos, mkStableId(pos, type)).setType(type);
+
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ return mkRef(pos, pre, sym);
+
+ case CompoundType(Type[] parents, Scope members):
+ if (parents.length == 1 && members.elems == Scope.Entry.NONE)
+ return mkType(pos, parents[0]);
+ else
+ return make.CompoundType(
+ pos, mkType(pos, parents), mkDefs(pos, members.elements()))
+ .setType(type);
+
+ case CovarType(Type tp):
+ return make.CovariantType(pos, mkType(pos, tp))
+ .setType(type);
+
+ case UnboxedType(_):
+ case UnboxedArrayType(_):
+ return make.Ident(pos, Name.fromString(type.toString()).toTypeName())
+ .setType(type);
+
+ default:
+ throw new ApplicationError("illegal type", type);
+ }
+ }
+
+ /** Build and attribute tree array corresponding to given type array.
+ */
+ public Tree[] mkType(int pos, Type[] types) {
+ Tree[] res = new Tree[types.length];
+ for (int i = 0; i < types.length; i++) {
+ res[i] = mkType(pos, types[i]);
+ }
+ return res;
+ }
+
+ /** Build and attribute tree corresponding to symbol's declaration.
+ */
+ public Tree mkDef(int pos, Symbol sym) {
+ switch (sym.kind) {
+ case ERROR:
+ return make.Bad(pos).setSymbol(Symbol.ERROR).setType(Type.ErrorType);
+ case TYPE: case ALIAS:
+ return TypeDef(pos, sym);
+ case VAL:
+ if (sym.isMethod()) return DefDef(pos, sym, Tree.Empty);
+ else return Param(pos, sym);
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** Build and attribute tree array corresponding to given symbol's declarations.
+ */
+ public Tree[] mkDefs(int pos, Symbol[] syms) {
+ Tree[] res = new Tree[syms.length];
+ for (int i = 0; i < syms.length; i++) {
+ res[i] = mkDef(pos, syms[i]);
+ }
+ return res;
+ }
+
+ /** Build a tree to be used as a base class constructor for a template.
+ */
+ public Tree mkParentConstr(int pos, Type parentType, Type root) {
+ switch (parentType) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ Tree ref = mkRef(pos, pre, sym.constructor());
+ Tree constr = (args.length == 0) ? ref
+ : TypeApply(ref, mkType(sym.pos, args));
+ switch (parentType) {
+ case MethodType(Symbol[] params, Type restpe):
+ assert params.length == 0 : parentType;
+ return Apply(constr, Tree.EMPTY_ARRAY);
+ default:
+ return constr;
+ }
+ default:
+ throw global.fail("invalid parent type", parentType);
+ }
+ }
+
+ /** Build an array of trees to be used as base classes for a template.
+ */
+ public Tree[] mkParentConstrs(int pos, Type[] parents, Type root) {
+ Tree[] constrs = new Tree[parents.length];
+ for (int i = 0; i < parents.length; ++i)
+ constrs[i] = mkParentConstr(pos, parents[i], root);
+ return constrs;
+ }
+
+ /** Build parameter sections corresponding to type.
+ */
+ public ValDef[][] mkParams(int pos, Type type) {
+ switch (type) {
+ case PolyType(Symbol[] tparams, Type restype):
+ return mkParams(pos, restype);
+ case MethodType(Symbol[] vparams, Type restype):
+ ValDef[] params1 = mkParams(pos, vparams);
+ ValDef[][] paramss = mkParams(pos, restype);
+ if (paramss.length == 0) {
+ return new ValDef[][]{params1};
+ } else {
+ ValDef[][] paramss1 = new ValDef[paramss.length + 1][];
+ paramss1[0] = params1;
+ System.arraycopy(paramss, 0, paramss1, 1, paramss.length);
+ return paramss1;
+ }
+ default:
+ return new ValDef[][]{};
+ }
+ }
+
+ /** Build parameter section corresponding to given array of symbols .
+ */
+ public ValDef[] mkParams(int pos, Symbol[] symbols) {
+ ValDef[] res = new ValDef[symbols.length];
+ for (int i = 0; i < symbols.length; i++) {
+ res[i] = Param(pos, symbols[i]);
+ }
+ return res;
+ }
+
+ /** Build type parameter section corresponding to given array of symbols .
+ */
+ public TypeDef[] mkTypeParams(int pos, Symbol[] symbols) {
+ TypeDef[] res = new TypeDef[symbols.length];
+ for (int i = 0; i < symbols.length; i++) {
+ res[i] = (TypeDef)TypeDef(pos, symbols[i]);
+ }
+ return res;
+ }
+
+ /** Build type definition corresponding to given symbol .
+ */
+ public TypeDef TypeDef(int pos, Symbol sym) {
+ Global.instance.nextPhase();
+ Type symtype = sym.info();
+ Global.instance.prevPhase();
+ return (TypeDef) make.TypeDef(
+ pos,
+ sym.flags & SOURCEFLAGS,
+ sym.name,
+ mkTypeParams(pos, sym.typeParams()),
+ mkType(pos, symtype))
+ .setSymbol(sym).setType(definitions.UNIT_TYPE);
+ }
+
+ public Tree TypeDef(Symbol sym) {
+ return TypeDef(sym.pos, sym);
+ }
+
+ /** Build parameter
+ */
+ public ValDef Param(int pos, Symbol sym) {
+ return (ValDef)ValDef(pos, sym, Tree.Empty);
+ }
+
+ public ValDef Param(Symbol sym) {
+ return Param(sym.pos, sym);
+ }
+
+ /** Build and attribute block with given statements, starting
+ * at given position. The type is the type of the last
+ * statement in the block.
+ */
+ public Tree Block(int pos, Tree[] stats) {
+ Type tp = (stats.length == 0) ? definitions.UNIT_TYPE
+ : stats[stats.length - 1].type;
+ return make.Block(pos, stats).setType(tp);
+ }
+
+ /** Build and attribute non-empty block with given statements.
+ */
+ public Tree Block(Tree[] stats) {
+ return Block(stats[0].pos, stats);
+ }
+
+ public Tree Typed(Tree tree, Type tp) {
+ return make.Typed(tree.pos, tree, mkType(tree.pos, tp)).setType(tp);
+ }
+
+ /** Build and attribute the assignment lhs = rhs
+ */
+ public Tree Assign(int pos, Tree lhs, Tree rhs) {
+ return make.Assign(pos, lhs, rhs).setType(definitions.UNIT_TYPE);
+ }
+
+ public Tree Assign(Tree lhs, Tree rhs) {
+ return Assign(lhs.pos, lhs, rhs);
+ }
+
+ /** Build and attribute new B, given constructor expression B.
+ */
+ public Tree New(Tree constr) {
+ Template templ = make.Template(
+ constr.pos, new Tree[]{constr}, Tree.EMPTY_ARRAY);
+ templ.setType(constr.type);
+ templ.setSymbol(localDummy(constr.pos, Symbol.NONE));
+ return make.New(constr.pos, templ).setType(constr.type); }
+
+ /** Build an allocation new P.C[TARGS](ARGS)
+ * given a (singleton) type P, class C, type arguments TARGS and arguments ARGS
+ */
+ public Tree New(int pos, Type pre, Symbol clazz,
+ Type[] targs, Tree[] args) {
+ Tree constr = mkRef(pos, pre, clazz.constructor());
+ if (targs.length != 0) constr = TypeApply(constr, mkType(pos, targs));
+ Tree base = Apply(constr, args);
+ return New(base);
+ }
+
+ /** Build a monomorphic allocation new P.C(ARGS)
+ * given a prefix P, class C and arguments ARGS
+ */
+ public Tree New(int pos, Type pre, Symbol clazz, Tree[] args) {
+ return New(pos, pre, clazz, Type.EMPTY_ARRAY, args);
+ }
+
+ /** Build and attribute application node with given function
+ * and argument trees.
+ */
+ public Tree Apply(int pos, Tree fn, Tree[] args) {
+ switch (fn.type) {
+ case Type.MethodType(Symbol[] vparams, Type restpe):
+ return make.Apply(pos, fn, args).setType(restpe);
+ default:
+ throw new ApplicationError("method type required", fn.type);
+ }
+ }
+
+ public Tree Apply(Tree fn, Tree[] args) {
+ return Apply(fn.pos, fn, args);
+ }
+
+ /** Build and attribute type application node with given function
+ * and argument trees.
+ */
+ public Tree TypeApply(int pos, Tree fn, Tree[] args) {
+ switch (fn.type) {
+ case Type.PolyType(Symbol[] tparams, Type restpe):
+ return make.TypeApply(pos, fn, args)
+ .setType(restpe.subst(tparams, Tree.typeOf(args)));
+ default:
+ throw new ApplicationError("poly type required", fn.type);
+ }
+ }
+
+ public Tree TypeApply(Tree fn, Tree[] args) {
+ return TypeApply(fn.pos, fn, args);
+ }
+
+ /** Build and applied type node with given function
+ * and argument trees.
+ */
+ public Tree AppliedType(int pos, Tree fn, Tree[] args) {
+ return make.AppliedType(pos, fn, args)
+ .setType(Type.appliedType(fn.type, Tree.typeOf(args)));
+ }
+
+ public Tree AppliedType(Tree fn, Tree[] args) {
+ return AppliedType(fn.pos, fn, args);
+ }
+
+ /** Build and attribute select node of given symbol.
+ * It is assumed that the prefix is not empty.
+ */
+ public Tree Select(int pos, Tree qual, Symbol sym) {
+ assert sym.kind != NONE;
+ Global.instance.nextPhase();
+ Type symtype = qual.type.memberType(sym);
+ Global.instance.prevPhase();
+ if (sym.kind == VAL && qual.type.isStable() && symtype.isObjectType())
+ symtype = Type.singleType(qual.type, sym);
+ return make.Select(pos, qual, sym.name)
+ .setSymbol(sym).setType(symtype);
+ }
+
+ public Tree Select(Tree qual, Symbol sym) {
+ return Select(qual.pos, qual, sym);
+ }
+
+ public Tree Select(Tree qual, Name name) {
+ Symbol sym = qual.type.lookup(name);
+ assert (sym.kind != NONE && sym != Symbol.ERROR) : name + " from " + qual.type;
+ return Select(qual, sym);
+ }
+
+ /** Build and attribute ident node with given symbol.
+ */
+ public Tree Ident(int pos, Symbol sym) {
+ Global.instance.nextPhase();
+ Type symtype = sym.type();
+ Global.instance.prevPhase();
+ if (sym.kind == VAL && symtype.isObjectType())
+ symtype = Type.singleType(sym.owner().thisType(), sym);
+ return make.Ident(pos, sym.name)
+ .setSymbol(sym).setType(symtype);
+ }
+
+ public Tree Ident(Symbol sym) {
+ return Ident(sym.pos, sym);
+ }
+
+ /** Build and attribute this node with given symbol.
+ */
+ public Tree This(int pos, Symbol sym) {
+ return make.This(pos, Ident(pos, sym)).setType(sym.thisType());
+ }
+
+ /** Build and attribute super node with given type.
+ */
+ public Tree Super(int pos, Type type) {
+ return make.Super(pos, mkType(pos, type)).setType(type);
+ }
+
+ /** Build and attribute value/variable/let definition node whose signature
+ * corresponds to given symbol and which has given rhs.
+ */
+ public Tree ValDef(int pos, Symbol sym, Tree rhs) {
+ Global.instance.nextPhase();
+ Type symtype = sym.type();
+ Global.instance.prevPhase();
+ return make.ValDef(pos,
+ sym.flags & SOURCEFLAGS,
+ sym.name,
+ mkType(pos, symtype),
+ rhs)
+ .setSymbol(sym).setType(definitions.UNIT_TYPE);
+ }
+
+ public Tree ValDef(Symbol sym, Tree rhs) {
+ return ValDef(sym.pos, sym, rhs);
+ }
+
+ /** Build and attribute value/variable/let definition node whose signature
+ * corresponds to given symbol and which has given body.
+ */
+ public Tree DefDef(int pos, Symbol sym, Tree body) {
+ Global.instance.nextPhase();
+ Type symtype = sym.type();
+ Global.instance.prevPhase();
+ return make.DefDef(pos,
+ sym.flags & SOURCEFLAGS,
+ sym.name,
+ mkTypeParams(pos, symtype.typeParams()),
+ mkParams(pos, symtype),
+ mkType(pos, symtype.resultType()),
+ body)
+ .setSymbol(sym).setType(definitions.UNIT_TYPE);
+ }
+
+ public Tree DefDef(Symbol sym, Tree rhs) {
+ return DefDef(sym.pos, sym, rhs);
+ }
+
+ /** Generate class definition from class symbol, parent constructors, and body.
+ */
+ public Tree ClassDef(int pos, Symbol clazz, Tree[] constrs, Tree[] body) {
+ Global.instance.nextPhase();
+ Type clazzinfo = clazz.info();
+ Type constrtype = clazz.constructor().info();
+ Global.instance.prevPhase();
+ switch (clazzinfo) {
+ case CompoundType(Type[] parents, Scope members):
+ Template templ = make.Template(pos, constrs, body);
+ templ.setType(clazzinfo);
+ templ.setSymbol(localDummy(pos, clazz.owner()));
+ return make.ClassDef(
+ pos,
+ clazz.flags & SOURCEFLAGS,
+ clazz.name,
+ mkTypeParams(pos, constrtype.typeParams()),
+ mkParams(pos, constrtype),
+ Tree.Empty,
+ templ)
+ .setSymbol(clazz).setType(definitions.UNIT_TYPE);
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ public Tree ClassDef(Symbol clazz, Tree[] constrs, Tree[] body) {
+ return ClassDef(clazz.pos, clazz, constrs, body);
+ }
+
+ /** Generate class definition from class symbol and body.
+ * All parents must by parameterless, or take unit parameters.
+ */
+ public Tree ClassDef(int pos, Symbol clazz, Tree[] body) {
+ Global.instance.nextPhase();
+ Type clazzinfo = clazz.info();
+ Global.instance.prevPhase();
+ return ClassDef(pos,
+ clazz,
+ mkParentConstrs(pos, clazzinfo.parents(), clazzinfo),
+ body);
+ }
+
+ public Tree ClassDef(Symbol clazz, Tree[] body) {
+ return ClassDef(clazz.pos, clazz, body);
+ }
+
+ /** Build the expansion of (() => expr)
+ * This is:
+ * { class $clazz() extends scala.Function0 { def apply() = expr } ; new $clazz() }
+ */
+ public Tree mkUnitFunction(Tree expr, Type tp, Symbol owner) {
+ int pos = expr.pos;
+ Type f0t = definitions.functionType(Type.EMPTY_ARRAY, tp);
+
+ ClassSymbol clazz = new ClassSymbol(
+ pos, Names.ANON_CLASS_NAME.toTypeName(), owner, 0);
+ clazz.setInfo(Type.compoundType(new Type[]{f0t}, new Scope(), clazz));
+ clazz.constructor().setInfo(
+ Type.MethodType(
+ Symbol.EMPTY_ARRAY,
+ Type.TypeRef(owner.thisType(), clazz, Type.EMPTY_ARRAY)));
+
+ Symbol applyMeth = new TermSymbol(pos, Names.apply, clazz, FINAL)
+ .setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, tp));
+ clazz.info().members().enter(applyMeth);
+
+ Tree applyDef = DefDef(applyMeth, changeOwner(expr, owner, applyMeth));
+ Tree classDef = ClassDef(clazz, new Tree[]{applyDef});
+ Tree alloc = New(pos, Type.localThisType, clazz, Tree.EMPTY_ARRAY);
+ return Block(new Tree[]{classDef, alloc});
+ }
+
+ /** Change owner of all defined symbols from `prevOwner' to `newOwner'
+ */
+ public Tree changeOwner(Tree tree, final Symbol prevOwner, final Symbol newOwner) {
+ Transformer lifter = new Transformer(global, global.currentPhase) {
+ public Tree transform(Tree tree) {
+ if (TreeInfo.isDefinition(tree)) {
+ Symbol sym = tree.symbol();
+ if (sym != null && sym.owner() == prevOwner) {
+ sym.setOwner(newOwner);
+ if (sym.kind == Kinds.CLASS)
+ sym.constructor().setOwner(newOwner);
+ }
+ }
+ return super.transform(tree);
+ }
+ };
+ return lifter.transform(tree);
+ }
+}
diff --git a/sources/scalac/ast/TreeInfo.java b/sources/scalac/ast/TreeInfo.java
new file mode 100644
index 0000000000..81d9481f31
--- /dev/null
+++ b/sources/scalac/ast/TreeInfo.java
@@ -0,0 +1,136 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast;
+
+import scalac.ApplicationError;
+import scalac.util.Name;
+import scalac.symtab.Type;
+import scalac.symtab.Symbol;
+import scalac.symtab.Modifiers;
+
+public class TreeInfo {
+
+ public static boolean isTerm(Tree tree) {
+ return tree.isTerm();
+ }
+
+ public static boolean isType(Tree tree) {
+ return tree.isType();
+ }
+
+ public static boolean isOwnerDefinition(Tree tree) {
+ switch (tree) {
+ case PackageDef(_, _):
+ case ClassDef(_, _, _, _, _, _):
+ case ModuleDef(_, _, _, _):
+ case DefDef(_, _, _, _, _, _):
+ case Import(_, _):
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static boolean isDefinition(Tree tree) {
+ switch (tree) {
+ case PackageDef(_, _):
+ case ClassDef(_, _, _, _, _, _):
+ case ModuleDef(_, _, _, _):
+ case DefDef(_, _, _, _, _, _):
+ case ValDef(_, _, _, _):
+ case TypeDef(_, _, _, _):
+ case Import(_, _):
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static boolean isDeclaration(Tree tree) {
+ switch (tree) {
+ case DefDef(_, _, _, _, _, Tree rhs):
+ return rhs == Tree.Empty;
+ case ValDef(_, _, _, Tree rhs):
+ return rhs == Tree.Empty;
+ case TypeDef(_, _, _, _):
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** Is tree a pure definition?
+ */
+ public static boolean isPureDef(Tree tree) {
+ switch (tree) {
+ case ClassDef(_, _, _, _, _, _):
+ case ModuleDef(_, _, _, _):
+ case DefDef(_, _, _, _, _, _):
+ case TypeDef(_, _, _, _):
+ case Import(_, _):
+ return true;
+ case ValDef(int mods, _, _, Tree rhs):
+ return (mods & Modifiers.MUTABLE) == 0 && isPureExpr(rhs);
+ default:
+ return false;
+ }
+ }
+
+ /** Is tree a stable & pure expression?
+ */
+ public static boolean isPureExpr(Tree tree) {
+ switch (tree) {
+ case Empty:
+ case This(_):
+ case Super(_):
+ return true;
+ case Ident(_):
+ return tree.type.isStable();
+ case Select(Tree qual, _):
+ return tree.type.isStable() && isPureExpr(qual);
+ case Typed(Tree expr, _):
+ return isPureExpr(expr);
+ case Literal(_):
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** Is tree a pure constructor?
+ * //todo: update
+ */
+ public static boolean isPureConstr(Tree tree) {
+ switch (tree) {
+ case Ident(_):
+ return tree.symbol() != null && tree.symbol().isPrimaryConstructor();
+ case Select(Tree qual, _):
+ return isPureExpr(qual) &&
+ tree.symbol() != null && tree.symbol().isPrimaryConstructor();
+ case TypeApply(Tree constr, _):
+ return isPureConstr(constr);
+ default:
+ return false;
+ }
+ }
+
+ /** The method symbol of an application node, or Symbol.NONE, if none exists.
+ */
+ public static Symbol methSymbol(Tree tree) {
+ switch (tree) {
+ case Apply(Tree fn, _):
+ return methSymbol(fn);
+ case TypeApply(Tree fn, _):
+ return methSymbol(fn);
+ default:
+ if (tree.hasSymbol()) return tree.symbol();
+ else return Symbol.NONE;
+ }
+ }
+}
diff --git a/sources/scalac/ast/TreeList.java b/sources/scalac/ast/TreeList.java
new file mode 100644
index 0000000000..d35627c686
--- /dev/null
+++ b/sources/scalac/ast/TreeList.java
@@ -0,0 +1,71 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id
+\* */
+
+package scalac.ast;
+
+
+public final class TreeList {
+ Tree[] trees;
+ int len;
+
+ public TreeList(Tree[] ts) {
+ trees = ts;
+ len = ts.length;
+ }
+
+ public TreeList() {
+ this(new Tree[4]);
+ len = 0;
+ }
+
+ public void append(Tree tree) {
+ if (len == trees.length) {
+ Tree[] ts = new Tree[len * 2];
+ System.arraycopy(trees, 0, ts, 0, len);
+ trees = ts;
+ }
+ trees[len++] = tree;
+ }
+
+ public void append(Tree[] ts) {
+ for (int j = 0; j < ts.length; j++)
+ append(ts[j]);
+ }
+
+ public void append(TreeList tl) {
+ for (int j = 0; j < tl.len; j++)
+ append(tl.trees[j]);
+ }
+
+ public int length() {
+ return len;
+ }
+
+ public Tree get(int i) {
+ return trees[i];
+ }
+
+ public Tree first() {
+ return trees[0];
+ }
+
+ public Tree removeLast() {
+ return trees[--len];
+ }
+
+ public Tree[] toArray() {
+ Tree[] ts = new Tree[len];
+ System.arraycopy(trees, 0, ts, 0, len);
+ return ts;
+ }
+
+ public Tree[] copyTo(Tree[] ts) {
+ System.arraycopy(trees, 0, ts, 0, len);
+ return ts;
+ }
+}
diff --git a/sources/scalac/ast/parser/Parser.java b/sources/scalac/ast/parser/Parser.java
new file mode 100644
index 0000000000..cd491e2af9
--- /dev/null
+++ b/sources/scalac/ast/parser/Parser.java
@@ -0,0 +1,1704 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast.parser;
+
+import java.util.*;
+import scalac.*;
+import scalac.util.*;
+import scalac.symtab.Modifiers;
+import scalac.ast.*;
+import Tree.*;
+
+/** A recursive descent parser for the programming language Scala.
+ *
+ * @author Martin Odersky, Matthias Zenger
+ * @version 1.2
+ */
+public class Parser implements Tokens {
+
+ /** the lexical analyzer
+ */
+ Scanner s;
+
+ /** the tree factory
+ */
+ TreeFactory make;
+
+ public Parser(Unit unit) {
+ s = new Scanner(unit);
+ make = unit.global.make;
+ }
+
+ /** this is the general parse method
+ */
+ public Tree[] parse() {
+ Tree[] ts = compilationUnit();
+ accept(EOF);
+ return ts;
+ }
+
+/////// ERROR HANDLING //////////////////////////////////////////////////////
+
+ private void skip() {
+ //System.out.println("<skipping> " + s.token2string(s.token));//DEBUG
+ int nparens = 0;
+ int nbraces = 0;
+ while (true) {
+ switch (s.token) {
+ case EOF:
+ return;
+ case SEMI:
+ if (nparens == 0 && nbraces == 0)
+ return;
+ break;
+ case RPAREN:
+ nparens--;
+ break;
+ case RBRACE:
+ if (nbraces == 0)
+ return;
+ nbraces--;
+ break;
+ case LPAREN:
+ nparens++;
+ break;
+ case LBRACE:
+ nbraces++;
+ break;
+ }
+ //System.out.println("skipped: " + s.token2string(s.token));//DEBUG
+ s.nextToken();
+ }
+ }
+
+ Tree syntaxError(String msg, boolean skip) {
+ return syntaxError(s.pos, msg, skip);
+ }
+
+ Tree syntaxError(int pos, String msg, boolean skip) {
+ if (pos != s.errpos) {
+ s.unit.error(pos, msg);
+ s.errpos = pos;
+ }
+ if (skip) skip();
+ return make.Bad(pos);
+ }
+
+ int accept(int token) {
+ int pos = s.pos;
+ if (s.token != token) {
+ int errpos = ((s.pos >>> Position.LINESHIFT) >
+ (s.lastpos >>> Position.LINESHIFT)) ?
+ s.lastpos : s.pos;
+ syntaxError(errpos, s.token2string(token) + " expected but " +
+ s.token2string(s.token) + " found.", true);
+ }
+ if (s.token == token) s.nextToken();
+ return pos;
+ }
+
+/////// TOKEN CLASSES //////////////////////////////////////////////////////
+
+ boolean isModifier() {
+ return (s.token == ABSTRACT)
+ || (s.token == FINAL)
+ || (s.token == PRIVATE)
+ || (s.token == PROTECTED)
+// || (s.token == QUALIFIED)
+ || (s.token == OVERRIDE);
+ }
+
+ boolean isLocalClassModifier() {
+ return (s.token == ABSTRACT)
+ || (s.token == FINAL);
+ }
+
+ boolean isDefIntro() {
+ switch (s.token) {
+ case VAL: case VAR: case DEF: case CONSTR: case TYPE:
+ case MODULE: case CLASS: case CASECLASS: case TRAIT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ boolean isDclIntro() {
+ switch (s.token) {
+ case VAL: case VAR: case DEF: case CONSTR: case TYPE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ boolean isExprIntro() {
+ switch (s.token) {
+ case CHARLIT: case INTLIT: case LONGLIT:
+ case FLOATLIT: case DOUBLELIT: case STRINGLIT: case NULL:
+ case IDENTIFIER: case THIS: case SUPER:
+ case IF: case FOR: case NEW: case USCORE:
+ case LPAREN: case LBRACKET: case LBRACE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+/////// TREE CONSTRUCTION ////////////////////////////////////////////////////
+
+ /** Name supply
+ */
+ int fresh = 0;
+
+ Name fresh() {
+ return Name.fromString("x$" + (fresh++));
+ }
+
+ /** Create tree representing binary operation expression or pattern.
+ */
+ Tree makeBinop(boolean isExpr, int pos, Tree left, Name op, Tree right) {
+ if (isExpr) {
+ if (op.leftAssoc()) {
+ return make.Apply(pos,
+ make.Select(pos, left, NameTransformer.encode(op)),
+ new Tree[]{right});
+ } else {
+ Name x = fresh();
+ return make.Block(pos,
+ new Tree[]{
+ make.ValDef(pos, 0, x, Tree.Empty, left),
+ make.Apply(pos,
+ make.Select(pos, right, NameTransformer.encode(op)),
+ new Tree[]{make.Ident(left.pos, x)})});
+ }
+ } else {
+ return make.Apply(pos,
+ make.Ident(pos, NameTransformer.encode(op).toTypeName()),
+ new Tree[]{left, right});
+ }
+ }
+
+ /** Create tree representing type scala.Any
+ */
+ Tree scalaAnyType(int pos) {
+ return make.Select(pos, make.Ident(pos, Names.scala), Names.Any.toTypeName());
+ }
+
+ /** Create tree representing constructor scala.Object
+ */
+ Tree scalaObjectConstr(int pos) {
+ return make.Select(pos, make.Ident(pos, Names.scala), Names.Object.toConstrName());
+ }
+
+ /** 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.
+ */
+ Tree makeFor(Tree[] enums, Name mapName, Name flatmapName, Tree body) {
+ switch (enums[0]) {
+ case PatDef(int mods, Tree pat, Tree rhs):
+ if (enums.length == 1)
+ return makeFor1(enums[0].pos, mapName, pat, rhs, body);
+ Tree[] newenums = new Tree[enums.length - 1];
+ switch (enums[1]) {
+ case PatDef(int mods2, Tree pat2, Tree rhs2):
+ System.arraycopy(enums, 1, newenums, 0, newenums.length);
+ return makeFor1(enums[0].pos, flatmapName, pat, rhs,
+ makeFor(newenums, mapName, flatmapName, body));
+ default:
+ System.arraycopy(enums, 2, newenums, 1, newenums.length - 1);
+ newenums[0] = make.PatDef(
+ enums[0].pos, mods, pat,
+ makeFor1(enums[0].pos, Names.filter, pat, rhs, enums[1]));
+ return makeFor(newenums, mapName, flatmapName, body);
+ }
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ //where
+ Tree makeFor1(int pos, Name name, Tree pat, Tree rhs, Tree body) {
+ Tree cont;
+ switch (pat) {
+ case Ident(Name name1):
+ cont = make.Function(pos,
+ new Tree.ValDef[]{
+ (ValDef)
+ make.ValDef(pat.pos, Modifiers.PARAM, name1, Tree.Empty, Tree.Empty)},
+ body);
+ break;
+ default:
+ cont = make.Visitor(pos, new Tree.CaseDef[]{
+ (CaseDef)make.CaseDef(pos, pat, Tree.Empty, body)});
+ }
+ return make.Apply(pos, make.Select(pos, rhs, name), new Tree[]{cont});
+ }
+
+ /** Convert tree to formal parameter list
+ */
+ ValDef[] convertToParams(Tree t) {
+ switch (t) {
+ case Function(ValDef[] params, Tree.Empty):
+ return params;
+ case Ident(_):
+ case Typed(Ident(_), _):
+ return new ValDef[]{convertToParam(t)};
+ case Block(Tree[] stats):
+ if (stats.length == 0) return Tree.ExtValDef.EMPTY_ARRAY;
+ }
+ syntaxError(t.pos, "malformed formal parameter list", false);
+ return Tree.ExtValDef.EMPTY_ARRAY;
+ }
+
+ /** Convert list of trees to formal parameter list
+ */
+ ValDef[] convertToParams(Tree[] ts) {
+ ValDef[] res = new ValDef[ts.length];
+ for (int i = 0; i < res.length; i++)
+ res[i] = convertToParam(ts[i]);
+ return res;
+ }
+
+ /** Convert tree to formal parameter
+ */
+ ValDef convertToParam(Tree tree) {
+ switch (tree) {
+ case Ident(Name name):
+ return (ValDef)make.ValDef(
+ tree.pos, Modifiers.PARAM, name, Tree.Empty, Tree.Empty);
+ case Typed(Ident(Name name), Tree tpe):
+ return (ValDef)make.ValDef(
+ tree.pos, Modifiers.PARAM, name, tpe, Tree.Empty);
+ default:
+ Tree tpe = syntaxError(tree.pos, "not a legal formal parameter", false);
+ return (ValDef)make.ValDef(
+ tree.pos, Modifiers.PARAM, Names.ERROR, tpe, Tree.Empty);
+ }
+ }
+
+ /** Convert (qual)ident to type identifier
+ */
+ Tree convertToTypeId(Tree t) {
+ switch (t) {
+ case Ident(Name name):
+ return make.Ident(t.pos, name.toTypeName());
+ case Select(Tree qual, Name name):
+ return make.Select(t.pos, qual, name.toTypeName());
+ default:
+ return t;
+ }
+ }
+
+ /** Convert (qual)ident to constructor identifier
+ */
+ Tree convertToConstr(Tree t) {
+ switch (t) {
+ case Apply(Tree fn, Tree[] args):
+ return make.Apply(t.pos, convertToConstr(fn), args);
+ case TypeApply(Tree fn, Tree[] args):
+ return make.TypeApply(t.pos, convertToConstr(fn), args);
+ case Ident(Name name):
+ return make.Ident(t.pos, name.toConstrName());
+ case Select(Tree qual, Name name):
+ return make.Select(t.pos, qual, name.toConstrName());
+ default:
+ return syntaxError(t.pos, "class constructor expected", false);
+ }
+ }
+
+/////// OPERAND/OPERATOR STACK /////////////////////////////////////////////////
+
+ Tree[] operands = new Tree[8];
+ int[] positions = new int[8];
+ Name[] operators = new Name[8];
+ int sp = 0;
+
+ void push(Tree od, int pos, Name op) {
+ if (sp == operands.length) {
+ Tree[] operands1 = new Tree[sp * 2];
+ System.arraycopy(operands, 0, operands1, 0, sp);
+ operands = operands1;
+ int[] positions1 = new int[sp * 2];
+ System.arraycopy(positions, 0, positions1, 0, sp);
+ positions = positions1;
+ Name[] operators1 = new Name[sp * 2];
+ System.arraycopy(operators, 0, operators1, 0, sp);
+ operators = operators1;
+ }
+ operands[sp] = od;
+ positions[sp] = pos;
+ operators[sp] = op;
+ sp++;
+ }
+
+ Tree reduceStack(boolean isExpr, int base, Tree top,
+ int prec, boolean leftAssoc) {
+ if (sp != base &&
+ operators[sp-1].precedence() == prec &&
+ operators[sp-1].leftAssoc() != leftAssoc) {
+ syntaxError(
+ positions[sp-1],
+ "left- and right-associative operators with same precedence may not be mixed",
+ false);
+ }
+ while (sp != base &&
+ (prec < operators[sp-1].precedence() ||
+ (leftAssoc && prec == operators[sp-1].precedence()))) {
+ sp--;
+ top = makeBinop(isExpr, positions[sp], operands[sp], operators[sp], top);
+ }
+ return top;
+ }
+
+/////// IDENTIFIERS AND LITERALS ////////////////////////////////////////////////////////////
+
+ static final Name MINUS = Name.fromString("-");
+ static final Name PLUS = Name.fromString("+");
+ static final Name BANG = Name.fromString("!");
+ static final Name TILDE = Name.fromString("~");
+
+ Name ident() {
+ if (s.token == IDENTIFIER) {
+ Name name = NameTransformer.encode(s.name);
+ s.nextToken();
+ return name;
+ } else {
+ accept(IDENTIFIER);
+ return Names.ERROR;
+ }
+ }
+
+ /** StableRef ::= StableId
+ * | [Ident `.'] this
+ * SimpleType ::= StableRef [`.' type]
+ */
+ Tree stableRef(boolean thisOK, boolean typeOK) {
+ Tree t;
+ if (s.token == THIS) {
+ t = make.This(s.skipToken(), Tree.Empty);
+ if (!thisOK || s.token == DOT)
+ t = selectors(accept(DOT), t, typeOK);
+ } else {
+ t = make.Ident(s.pos, ident());
+ if (s.token == DOT) {
+ int pos = s.skipToken();
+ if (s.token == THIS) {
+ s.nextToken();
+ t = make.This(pos, convertToTypeId(t));
+ if (!thisOK || s.token == DOT)
+ t = selectors(accept(DOT), t, typeOK);
+ } else {
+ t = selectors(pos, t, typeOK);
+ }
+ }
+ }
+ return t;
+ }
+
+ Tree selectors(int pos, Tree t, boolean typeOK) {
+ if (typeOK && s.token == TYPE) {
+ s.nextToken();
+ return make.SingletonType(pos, t);
+ } else {
+ t = make.Select(pos, t, ident());
+ if (s.token == DOT) {
+ t = selectors(s.skipToken(), t, typeOK);
+ }
+ return t;
+ }
+ }
+
+ /** StableId ::= [[Ident `.'] this `.'] {Id `.'} Id
+ */
+ Tree stableId() {
+ return stableRef(false, false);
+ }
+
+ /** QualId ::= Id {`.' Id}
+ */
+ Tree qualId() {
+ Tree id = make.Ident(s.pos, ident());
+ if (s.token == DOT) return selectors(s.skipToken(), id, false);
+ else return id;
+ }
+
+ /** SimpleExpr ::= literal
+ * | null
+ */
+ Tree literal() {
+ Tree t;
+ switch (s.token) {
+ case CHARLIT:
+ t = make.Literal(s.pos, new Character((char)s.intVal));
+ break;
+ case INTLIT:
+ t = make.Literal(s.pos, new Integer((int)s.intVal));
+ break;
+ case LONGLIT:
+ t = make.Literal(s.pos, new Long(s.intVal));
+ break;
+ case FLOATLIT:
+ t = make.Literal(s.pos, new Float((float)s.floatVal));
+ break;
+ case DOUBLELIT:
+ t = make.Literal(s.pos, new Double(s.floatVal));
+ break;
+ case STRINGLIT:
+ t = make.Literal(s.pos, s.name.toString());
+ break;
+ case NULL:
+ t = make.Ident(s.pos, Names.null_);
+ break;
+ default:
+ return syntaxError("illegal literal", true);
+ }
+ s.nextToken();
+ return t;
+ }
+
+//////// TYPES ///////////////////////////////////////////////////////////////
+
+ /** TypedOpt ::= [`:' Type]
+ */
+ Tree typedOpt() {
+ if (s.token == COLON) {
+ s.nextToken();
+ return type();
+ } else {
+ return Tree.Empty;
+ }
+ }
+
+ /** Types ::= Type {`,' Type}
+ */
+ Tree[] types() {
+ TreeList ts = new TreeList();
+ ts.append(type());
+ while (s.token == COMMA) {
+ s.nextToken();
+ ts.append(type());
+ }
+ return ts.toArray();
+ }
+
+ /** Type ::= Type1 `=>' Type
+ * | `(' [Types] `)' `=>' Type
+ * | Type1
+ */
+ Tree type() {
+ Tree t;
+ if (s.token == LPAREN) {
+ s.nextToken();
+ if (s.token == RPAREN) {
+ s.nextToken();
+ int pos = accept(ARROW);
+ return make.FunType(pos, Tree.EMPTY_ARRAY, type());
+ } else {
+ t = type();
+ if (s.token == COMMA) {
+ s.nextToken();
+ TreeList ts = new TreeList();
+ ts.append(t);
+ ts.append(types());
+ accept(RPAREN);
+ int pos = accept(ARROW);
+ return make.FunType(pos, ts.toArray(), type());
+ } else {
+ accept(RPAREN);
+ }
+ }
+ } else {
+ t = type1();
+ }
+ if (s.token == ARROW)
+ return make.FunType(s.skipToken(), new Tree[]{t}, type());
+ else
+ return t;
+ }
+
+ /** Type1 ::= SimpleType {with SimpleType} [with Refinement]
+ */
+ Tree type1() {
+ int pos = s.pos;
+ Tree t = simpleType();
+ if (s.token == WITH) {
+ TreeList ts = new TreeList();
+ ts.append(t);
+ while (s.token == WITH) {
+ s.nextToken();
+ if (s.token == LBRACE)
+ return make.CompoundType(pos, ts.toArray(), refinement());
+ else
+ ts.append(simpleType());
+ }
+ return make.CompoundType(pos, ts.toArray(), Tree.EMPTY_ARRAY);
+ } else {
+ return t;
+ }
+ }
+
+ /** SimpleType ::= SimpleType TypeArgs
+ * | SimpleType `#' Id
+ * | StableId
+ * | StableRef `.' type
+ * | `[' Types `]'
+ * | `(' Type `)'
+ */
+ Tree simpleType() {
+ int pos = s.pos;
+ Tree t;
+ if (s.token == LPAREN) {
+ s.nextToken();
+ t = type();
+ accept(RPAREN);
+ } else if (s.token == LBRACKET) {
+ s.nextToken();
+ Tree[] ts = types();
+ accept(RBRACKET);
+ t = make.TupleType(pos, ts);
+ } else {
+ t = convertToTypeId(stableRef(false, true));
+ }
+ while (true) {
+ if (s.token == HASH)
+ t = make.SelectFromType(s.skipToken(), t, ident().toTypeName());
+ else if (s.token == LBRACKET)
+ t = make.AppliedType(pos, t, varTypeArgs());
+ else break;
+ }
+ return t;
+ }
+
+ /** TypeArgs ::= `[' Types `]'
+ */
+ Tree[] typeArgs() {
+ accept(LBRACKET);
+ Tree[] ts = types();
+ accept(RBRACKET);
+ return ts;
+ }
+
+ /** VarTypeArgs ::= `[' VarType {`,' VarType} `]'
+ */
+ Tree[] varTypeArgs() {
+ int pos = accept(LBRACKET);
+ TreeList ts = new TreeList();
+ ts.append(varType());
+ while (s.token == COMMA) {
+ s.nextToken();
+ ts.append(varType());
+ }
+ accept(RBRACKET);
+ return ts.toArray();
+ }
+
+ /** VarType ::= [`+'] Type
+ */
+ Tree varType() {
+ int pos = s.pos;
+ if (s.token == IDENTIFIER && s.name == PLUS)
+ return make.CovariantType(s.skipToken(), type());
+ else
+ return type();
+ }
+
+//////// EXPRESSIONS ////////////////////////////////////////////////////////
+
+ /** EqualsExpr ::= `=' Expr
+ */
+ Tree equalsExpr() {
+ accept(EQUALS);
+ return expr();
+ }
+
+ /** Exprs ::= Expr {`,' Expr}
+ */
+ Tree[] exprs() {
+ TreeList ts = new TreeList();
+ ts.append(expr());
+ while (s.token == COMMA) {
+ s.nextToken();
+ ts.append(expr());
+ }
+ return ts.toArray();
+ }
+
+ /** Expr ::= [Bindings `=>'] Expr
+ * | if `(' Expr `)' Expr [[`;'] else Expr]
+ * | for `(' Enumerators `)' [do | yield] Expr
+ * | Designator `=' Expr
+ * | SimpleExpr ArgumentExprs `=' Expr
+ * | PostfixExpr [`:' Type1 | as Type1 | is Type1]
+ * Bindings ::= Id [`:' Type1]
+ * | `(' [Binding {`,' Binding}] `)'
+ */
+ Tree expr() {
+ if (s.token == IF) {
+ int pos = s.skipToken();
+ accept(LPAREN);
+ Tree cond = expr();
+ accept(RPAREN);
+ Tree thenp = expr();
+ Tree elsep = Tree.Empty;
+ if (s.token == ELSE) {
+ s.nextToken();
+ elsep = expr();
+ } else {
+ elsep = Tree.Empty;
+ }
+ return make.If(pos, cond, thenp, elsep) ;
+ } else if (s.token == FOR) {
+ s.nextToken();
+ accept(LPAREN);
+ Tree[] enums = enumerators();
+ accept(RPAREN);
+ if (s.token == DO) {
+ s.nextToken();
+ return makeFor(enums, Names.foreach, Names.foreach, expr());
+ } else if (s.token == YIELD) {
+ s.nextToken();
+ return makeFor(enums, Names.map, Names.flatmap, expr());
+ } else {
+ return syntaxError("`do' or `yield' expected", true);
+ }
+// } else if (s.token == ARROW) {
+// return make.Function(s.skipToken(), new ValDef[]{}, expr());
+ } else {
+ Tree t = postfixExpr();
+ if (s.token == EQUALS) {
+ switch (t) {
+ case Ident(_):
+ case Select(_, _):
+ case Apply(_, _):
+ t = make.Assign(s.skipToken(), t, expr());
+ }
+ } else if (s.token == COLON) {
+ int pos = s.skipToken();
+ Tree tp = type1();
+ t = make.Typed(pos, t, tp);
+ } else if (s.token == AS || s.token == IS) {
+ Name op = (s.token == AS) ? Names.as : Names.is;
+ int pos = s.skipToken();
+ t = make.TypeApply(pos, make.Select(pos, t, op), new Tree[]{type1()});
+ }
+ if (s.token == ARROW) {
+ t = make.Function(s.skipToken(), convertToParams(t), expr());
+ }
+ return t;
+ }
+ }
+
+ /** PostfixExpr ::= InfixExpr [Id]
+ * InfixExpr ::= PrefixExpr
+ * | InfixExpr Id InfixExpr
+ */
+ Tree postfixExpr() {
+ int base = sp;
+ Tree top = prefixExpr();
+ while (s.token == IDENTIFIER) {
+ top = reduceStack(
+ true, base, top, s.name.precedence(), s.name.leftAssoc());
+ push(top, s.pos, s.name);
+ ident();
+ if (isExprIntro()) {
+ top = prefixExpr();
+ } else {
+ sp--;
+ int pos = positions[sp];
+ Name postOp = operators[sp];
+ top = reduceStack(true, base, operands[sp], 0, true);
+ return make.Select(pos, top, postOp);
+ }
+ }
+ return reduceStack(true, base, top, 0, true);
+ }
+
+ /** PrefixExpr ::= [op] SimpleExpr
+ */
+ Tree prefixExpr() {
+ Tree t;
+ if (s.token == IDENTIFIER &&
+ (s.name == MINUS ||
+ s.name == PLUS ||
+ s.name == TILDE ||
+ s.name == BANG)) {
+ Name name = ident();
+ t = make.Select(s.pos, simpleExpr(), name);
+ } else {
+ t = simpleExpr();
+ }
+ return t;
+ }
+
+ /* SimpleExpr ::= literal
+ * | null
+ * | StableRef
+ * | super `.' Id
+ * | SimpleExpr `.' Id
+ * | `(' [Expr] `)'
+ * | `[' [Exprs] `]'
+ * | BlockExpr
+ * | SimpleExpr `@' TypeArgs
+ * | SimpleExpr ArgumentExprs
+ * | new Template
+ * | `_'
+ */
+ Tree simpleExpr() {
+ Tree t;
+ switch (s.token) {
+ case CHARLIT:
+ case INTLIT:
+ case LONGLIT:
+ case FLOATLIT:
+ case DOUBLELIT:
+ case STRINGLIT:
+ case NULL:
+ t = literal();
+ break;
+ case IDENTIFIER:
+ case THIS:
+ t = stableRef(true, false);
+ break;
+ case SUPER:
+ int pos = s.skipToken();
+ t = make.Select(accept(DOT), make.Super(pos, Tree.Empty), ident());
+ break;
+ case LPAREN:
+ int pos = s.skipToken();
+ if (s.token == RPAREN) {
+ s.nextToken();
+ t = make.Block(pos, Tree.EMPTY_ARRAY);
+ } else {
+ t = expr();
+ if (s.token == COMMA) {
+ int commapos = s.skipToken();
+ TreeList ts = new TreeList();
+ ts.append(t);
+ ts.append(exprs());
+ accept(RPAREN);
+ if (s.token == ARROW) {
+ t = make.Function(pos, convertToParams(ts.toArray()), Tree.Empty);
+ } else {
+ t = syntaxError(commapos, "`)' expected", false);
+ }
+ } else {
+ accept(RPAREN);
+ }
+ }
+ break;
+ case LBRACKET:
+ int pos = s.skipToken();
+ Tree[] ts;
+ if (s.token == RBRACKET) ts = Tree.EMPTY_ARRAY;
+ else ts = exprs();
+ t = make.Tuple(pos, ts);
+ accept(RBRACKET);
+ break;
+ case LBRACE:
+ t = blockExpr();
+ break;
+ case NEW:
+ t = make.New(s.skipToken(), template());
+ break;
+ default:
+ return syntaxError("illegal start of expression", true);
+ }
+ while (true) {
+ switch (s.token) {
+ case DOT:
+ t = make.Select(s.skipToken(), t, ident());
+ break;
+ case AT:
+ int pos = s.skipToken();
+ t = make.TypeApply(pos, t, typeArgs());
+ break;
+ case LPAREN:
+ case LBRACKET:
+ case LBRACE:
+ t = make.Apply(s.pos, t, argumentExprs());
+ break;
+ default:
+ return t;
+ }
+ }
+ }
+
+ /** ArgumentExprs ::= `(' [Exprs] `)'
+ * | `[' [Exprs] `]'
+ * | BlockExpr
+ */
+ Tree[] argumentExprs() {
+ Tree[] ts = Tree.EMPTY_ARRAY;
+ if (s.token == LBRACE) {
+ ts = new Tree[]{blockExpr()};
+ } else if (s.token == LBRACKET) {
+ int pos = s.skipToken();
+ if (s.token != RBRACKET)
+ ts = exprs();
+ accept(RBRACKET);
+ ts = new Tree[]{make.Tuple(pos, ts)};
+ } else {
+ accept(LPAREN);
+ if (s.token != RPAREN)
+ ts = exprs();
+ accept(RPAREN);
+ }
+ return ts;
+ }
+
+ /** BlockExpr ::= `{' CaseClause {CaseClause} `}'
+ * | `{' Block `}'
+ */
+ Tree blockExpr() {
+ Tree res;
+ int pos = accept(LBRACE);
+ if (s.token == CASE) {
+ TreeList stats = new TreeList();
+ do {
+ stats.append(caseClause());
+ } while (s.token == CASE);
+ res = make.Visitor(
+ pos, (CaseDef[]) stats.copyTo(new CaseDef[stats.length()]));
+ } else {
+ res = block(pos);
+ }
+ accept(RBRACE);
+ return res;
+ }
+
+ /** BlockConstr ::= `{' Block `}'
+ */
+ Tree blockConstr() {
+ int pos = accept(LBRACE);
+ Tree res = block(pos);
+ switch (res) {
+ case Block(Tree[] stats):
+ if (stats.length > 0)
+ stats[stats.length - 1] = convertToConstr(stats[stats.length - 1]);
+ else
+ syntaxError(res.pos, "class constructor expected", false);
+ }
+ accept(RBRACE);
+ return res;
+ }
+
+ /** Block ::= BlockStatSeq
+ */
+ Tree block(int pos) {
+ Tree[] stats = blockStatSeq(new TreeList());
+ if (stats.length == 1) return stats[0];
+ else return make.Block(pos, stats);
+ }
+
+ /** CaseClause ::= case Pattern [if `(' Expr `)'] `=>' Block
+ */
+ Tree caseClause() {
+ int pos = accept(CASE);
+ Tree pat = pattern();
+ Tree guard = Tree.Empty;
+ if (s.token == IF) {
+ s.nextToken();
+ accept(LPAREN);
+ guard = expr();
+ accept(RPAREN);
+ }
+ accept(ARROW);
+ return make.CaseDef(pos, pat, guard, block(s.pos));
+ }
+
+ /** Enumerators ::= Generator {`;' Enumerator}
+ * Enumerator ::= Generator
+ * | Expr
+ */
+ Tree[] enumerators() {
+ TreeList enums = new TreeList();
+ enums.append(generator());
+ while (s.token == SEMI) {
+ s.nextToken();
+ if (s.token == VAL) enums.append(generator());
+ else enums.append(expr());
+ }
+ return enums.toArray();
+ }
+
+ /** Generator ::= val Pattern `<-' Expr
+ */
+ Tree generator() {
+ int pos = accept(VAL);
+ Tree p = pattern();
+ accept(LARROW);
+ return make.PatDef(pos, 0, p, expr());
+ }
+
+//////// PATTERNS ////////////////////////////////////////////////////////////
+
+ /** Patterns ::= Pattern {`,' Pattern}
+ */
+ Tree[] patterns() {
+ TreeList ts = new TreeList();
+ ts.append(pattern());
+ while (s.token == COMMA) {
+ s.nextToken();
+ ts.append(pattern());
+ }
+ return ts.toArray();
+ }
+
+ /** Pattern ::= varid `:' Type1
+ * | `_' `:' Type1
+ * | SimplePattern {Id SimplePattern}
+ */
+ Tree pattern() {
+ int base = sp;
+ Tree top = simplePattern();
+ if (s.token == COLON) {
+ switch (top) {
+ case Ident(Name name):
+ if (name.isVariable())
+ return make.Typed(s.skipToken(), top, type1());
+ }
+ }
+ while (s.token == IDENTIFIER) {
+ top = reduceStack(
+ false, base, top, s.name.precedence(), s.name.leftAssoc());
+ push(top, s.pos, s.name);
+ ident();
+ top = simplePattern();
+ }
+ return reduceStack(false, base, top, 0, true);
+ }
+
+ /** SimplePattern ::= varid
+ * | `_'
+ * | literal
+ * | null
+ * | StableId {ArgumentPatterns}
+ * | `(' Pattern `)'
+ * | `[' [Patterns] `]'
+ */
+ Tree simplePattern() {
+ switch (s.token) {
+ case IDENTIFIER:
+ case THIS:
+ Tree t = stableId();
+ while (s.token == LPAREN || s.token == LBRACKET) {
+ t = make.Apply(s.pos, convertToConstr(t), argumentPatterns());
+ }
+ return t;
+ case USCORE:
+ return make.Ident(s.skipToken(), Names.WILDCARD);
+ case CHARLIT:
+ case INTLIT:
+ case LONGLIT:
+ case FLOATLIT:
+ case DOUBLELIT:
+ case STRINGLIT:
+ case NULL:
+ return literal();
+ case LPAREN:
+ s.nextToken();
+ Tree t = pattern();
+ accept(RPAREN);
+ return t;
+ case LBRACKET:
+ return tuplePattern();
+ default:
+ return syntaxError("illegal start of pattern", true);
+ }
+ }
+
+ /** SimplePattern ::= `[' [Patterns] ']'
+ */
+ Tree tuplePattern() {
+ int pos = accept(LBRACKET);
+ Tree[] ts;
+ if (s.token == RBRACKET) ts = Tree.EMPTY_ARRAY;
+ else ts = patterns();
+ accept(RBRACKET);
+ return make.Tuple(pos, ts);
+ }
+
+ /** ArgumentPatterns ::= `(' [Patterns] `)'
+ * | `[' [Patterns] `]'
+ */
+ Tree[] argumentPatterns() {
+ if (s.token == LBRACKET) {
+ return new Tree[]{tuplePattern()};
+ } else {
+ Tree[] ts = Tree.EMPTY_ARRAY;
+ accept(LPAREN);
+ if (s.token != RPAREN)
+ ts = patterns();
+ accept(RPAREN);
+ return ts;
+ }
+ }
+
+////////// MODIFIERS ////////////////////////////////////////////////////////////
+
+ /** Modifiers ::= {Modifier}
+ * Modifier ::= final
+ * | private
+ * | protected
+ * | override
+ * | abstract
+ */
+ int modifiers() {
+ int mods = 0;
+ while (true) {
+ int mod;
+ switch (s.token) {
+ case ABSTRACT:
+ mod = Modifiers.ABSTRACTCLASS;
+ break;
+ case FINAL:
+ mod = Modifiers.FINAL;
+ break;
+ case PRIVATE:
+ mod = Modifiers.PRIVATE;
+ break;
+ case PROTECTED:
+ mod = Modifiers.PROTECTED;
+ break;
+ case OVERRIDE:
+ mod = Modifiers.OVERRIDE;
+ break;
+ default:
+ return mods;
+ }
+ if ((mods & mod) != 0)
+ syntaxError(s.pos, "repeated modifier", false);
+ mods |= mod;
+ s.nextToken();
+ }
+ }
+
+ /** LocalClassModifiers ::= {LocalClassModifier}
+ * LocalClassModifier ::= final
+ * | private
+ */
+ int localClassModifiers() {
+ int mods = 0;
+ while (true) {
+ int mod;
+ switch (s.token) {
+ case ABSTRACT:
+ mod = Modifiers.ABSTRACTCLASS;
+ break;
+ case FINAL:
+ mod = Modifiers.FINAL;
+ break;
+ default:
+ return mods;
+ }
+ if ((mods & mod) != 0)
+ syntaxError(s.pos, "repeated modifier", false);
+ mods |= mod;
+ s.nextToken();
+ }
+ }
+
+//////// PARAMETERS //////////////////////////////////////////////////////////
+
+ /** ParamClauses ::= {ParamClause}
+ */
+ ValDef[][] paramClauses() {
+ ArrayList ts = new ArrayList();
+ while (s.token == LPAREN)
+ ts.add(paramClause());
+ return (ValDef[][])ts.toArray(new ValDef[ts.size()][]);
+ }
+
+ /** ParamClause ::= `(' [Param {`,' Param}] `)'
+ */
+ ValDef[] paramClause() {
+ int pos = accept(LPAREN);
+ TreeList params = new TreeList();
+ if (s.token != RPAREN) {
+ params.append(param());
+ while (s.token == COMMA) {
+ s.nextToken();
+ params.append(param());
+ }
+ }
+ accept(RPAREN);
+ return (ValDef[])params.copyTo(new ValDef[params.length()]);
+ }
+
+ /** Param ::= [def] Id `:' Type
+ */
+ ValDef param() {
+ int pos = s.pos;
+ int mods = Modifiers.PARAM;
+ if (s.token == DEF) {
+ mods |= Modifiers.DEF;
+ s.nextToken();
+ }
+ Name name = ident();
+ accept(COLON);
+ return (ValDef)make.ValDef(pos, mods, name, type(), Tree.Empty);
+ }
+
+ /** TypeParamClauseOpt ::= [`[' TypeSig {`,' TypeSig} `]']
+ */
+ TypeDef[] typeParamClauseOpt() {
+ TreeList params = new TreeList();
+ if (s.token == LBRACKET) {
+ s.nextToken();
+ params.append(typeParam());
+ while (s.token == COMMA) {
+ s.nextToken();
+ params.append(typeParam());
+ }
+ accept(RBRACKET);
+ }
+ return (TypeDef[])params.copyTo(new TypeDef[params.length()]);
+ }
+
+ /** TypeSig ::= Id [<: Type]
+ */
+ Tree typeParam() {
+ int pos = s.pos;
+ Name name = ident();
+ Tree tp;
+ if (s.token == SUBTYPE) {
+ s.nextToken();
+ tp = type();
+ } else {
+ tp = scalaAnyType(pos);
+ }
+ return make.TypeDef(pos, Modifiers.PARAM, name.toTypeName(),
+ Tree.ExtTypeDef.EMPTY_ARRAY, tp);
+ }
+
+//////// DEFS ////////////////////////////////////////////////////////////////
+
+ /** Import ::= import ImportRef {`,' ImportRef}
+ */
+ Tree[] importClause() {
+ accept(IMPORT);
+ TreeList ts = new TreeList();
+ ts.append(importRef());
+ while (s.token == COMMA) {
+ s.nextToken();
+ ts.append(importRef());
+ }
+ return ts.toArray();
+ }
+
+ /** ImportRef ::= StableId `.' (Id | `_' | ImportSelectors)
+ */
+ Tree importRef() {
+ Tree t;
+ int startpos = s.pos;
+ int pos;
+ if (s.token == THIS) {
+ t = make.This(s.skipToken(), Tree.Empty);
+ t = make.Select(accept(DOT), t, ident());
+ pos = accept(DOT);
+ } else {
+ t = make.Ident(s.pos, ident());
+ pos = accept(DOT);
+ if (s.token == THIS) {
+ s.nextToken();
+ t = make.This(pos, convertToTypeId(t));
+ t = make.Select(accept(DOT), t, ident());
+ pos = accept(DOT);
+ }
+ }
+ while (true) {
+ if (s.token == USCORE) {
+ s.nextToken();
+ return make.Import(startpos, t, new Name[]{Names.WILDCARD});
+ } else if (s.token == LBRACE) {
+ return make.Import(startpos, t, importSelectors());
+ } else {
+ Name name = ident();
+ if (s.token == DOT) {
+ t = make.Select(pos, t, name);
+ pos = accept(DOT);
+ } else {
+ return make.Import(startpos, t, new Name[]{name, name});
+ }
+ }
+ }
+ }
+
+ /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}'
+ */
+ Name[] importSelectors() {
+ LinkedList/*<Name>*/ names = new LinkedList();
+ accept(LBRACE);
+ boolean isLast = importSelector(names);
+ while (!isLast && s.token == COMMA) {
+ s.nextToken();
+ isLast = importSelector(names);
+ }
+ accept(RBRACE);
+ return (Name[])names.toArray(new Name[]{});
+ }
+
+ /** ImportSelector ::= Id [`=>' [Id | `_']]
+ */
+ boolean importSelector(LinkedList/*<Name>*/ names) {
+ if (s.token == USCORE) {
+ s.nextToken();
+ names.add(Names.WILDCARD);
+ return true;
+ } else {
+ Name name = ident();
+ names.add(name);
+ if (s.token == ARROW) {
+ s.nextToken();
+ if (s.token == USCORE) {
+ s.nextToken();
+ names.add(Names.WILDCARD);
+ } else {
+ names.add(ident());
+ }
+ } else {
+ names.add(name);
+ }
+ return false;
+ }
+ }
+
+ /** Def ::= val PatDef {`,' PatDef}
+ * | var VarDef {`,' VarDef}
+ * | def FunDef {`,' FunDef}
+ * | constr ConstrDef {`,' ConstrDef}
+ * | type TypeDef {`,' TypeDef}
+ * | TopDef
+ * Dcl ::= val ValSig {`,' ValSig}
+ * | var ValSig {`,' ValSig}
+ * | def FunSig {`,' FunSig}
+ * | constr ConstrSig {`,' ConstrSig}
+ * | type TypeSig {`,' TypeSig}
+ */
+ Tree[] defOrDcl(int mods) {
+ TreeList ts = new TreeList();
+ switch (s.token) {
+ case VAL:
+ do {
+ s.nextToken();
+ ts.append(patDefOrSig(mods));
+ } while (s.token == COMMA);
+ return ts.toArray();
+ case VAR:
+ do {
+ s.nextToken();
+ ts.append(varDefOrSig(mods));
+ } while (s.token == COMMA);
+ return ts.toArray();
+ case DEF:
+ do {
+ s.nextToken();
+ ts.append(funDefOrSig(mods));
+ } while (s.token == COMMA);
+ return ts.toArray();
+ case CONSTR:
+ do {
+ s.nextToken();
+ ts.append(constrDefOrSig(mods));
+ } while (s.token == COMMA);
+ return ts.toArray();
+ case TYPE:
+ do {
+ s.nextToken();
+ ts.append(typeDefOrSig(mods));
+ } while (s.token == COMMA);
+ return ts.toArray();
+ default:
+ return topDef(mods);
+ }
+ }
+
+ /** TopDef ::= ([case] class) ClassDef {`,' ClassDef}
+ * | trait TraitDef {`,' TraitDef}
+ * | module ModuleDef {`,' ModuleDef}
+ * LocalTopDef ::= class ClassDef {`,' ClassDef}
+ * | trait TraitDef {`,' TraitDef}
+ * | module ModuleDef {`,' ModuleDef}
+ */
+ Tree[] topDef(int mods) {
+ TreeList ts = new TreeList();
+ switch (s.token) {
+ case CLASS:
+ case CASECLASS:
+ case TRAIT:
+ if (s.token == CASECLASS) mods |= Modifiers.CASE;
+ else if (s.token == TRAIT) mods |= Modifiers.TRAIT | Modifiers.ABSTRACTCLASS;
+ do {
+ s.nextToken();
+ ts.append(classDef(mods));
+ } while (s.token == COMMA);
+ return ts.toArray();
+ case MODULE:
+ do {
+ s.nextToken();
+ ts.append(moduleDef(mods));
+ } while (s.token == COMMA);
+ return ts.toArray();
+ default:
+ return new Tree[]{syntaxError("illegal start of definition", true)};
+ }
+ }
+
+ /** PatDef ::= Pattern `=' Expr
+ * ValSig ::= Id `:' Type
+ */
+ Tree patDefOrSig(int mods) {
+ int pos = s.pos;
+ Tree pat = pattern();
+ Tree tp;
+ switch (pat) {
+ case Typed(Tree pat1, Tree tp1):
+ pat = pat1;
+ tp = tp1;
+ break;
+ default:
+ if (s.token == COLON) tp = typedOpt();
+ else tp = Tree.Empty;
+ }
+ switch (pat) {
+ case Ident(Name name):
+ if (tp == Tree.Empty || s.token == EQUALS)
+ return make.ValDef(pos, mods, name, tp, equalsExpr());
+ else
+ return make.ValDef(pos, mods | Modifiers.ABSTRACT, name, tp, Tree.Empty);
+ default:
+ return make.PatDef(pos, mods, pat, equalsExpr());
+ }
+ }
+
+ /** VarDef ::= Id [`:' Type] `=' Expr
+ * | Id `:' Type `=' `_'
+ * VarSig ::= Id `:' Type
+ */
+ Tree varDefOrSig(int mods) {
+ int pos = s.pos;
+ Name name = ident();
+ Tree type = typedOpt();
+ if (type == Tree.Empty || s.token == EQUALS) {
+ accept(EQUALS);
+ Tree rhs;
+ if (type != Tree.Empty && s.token == USCORE) {
+ rhs = Tree.Empty;
+ s.nextToken();
+ } else {
+ rhs = expr();
+ }
+ return make.ValDef(pos, mods | Modifiers.MUTABLE, name, type, rhs);
+ } else {
+ return make.ValDef(pos, mods | Modifiers.MUTABLE | Modifiers.ABSTRACT,
+ name, type, Tree.Empty);
+ }
+ }
+
+ /** FunDef ::= Id [TypeParamClause] {ParamClause} [`:' Type]
+ * (`=' Expr | BlockExpr)
+ * FunSig ::= Id [TypeParamClause] {ParamClause} `:' Type
+ */
+ Tree funDefOrSig(int mods) {
+ int pos = s.pos;
+ Name name = ident();
+ TypeDef[] tparams = typeParamClauseOpt();
+ ValDef[][] vparams = paramClauses();
+ Tree restype = typedOpt();
+ if (s.token == LBRACE)
+ return make.DefDef(pos, mods, name, tparams, vparams,
+ restype, blockExpr());
+ else if (s.token == EQUALS || restype == Tree.Empty)
+ return make.DefDef(pos, mods, name, tparams, vparams,
+ restype, equalsExpr());
+ else
+ return make.DefDef(pos, mods | Modifiers.ABSTRACT, name,
+ tparams, vparams, restype, Tree.Empty);
+ }
+
+ /* ConstrDef ::= Id [TypeParamClause] [ParamClause] [`:' Type]
+ * (`=' Constr | `=' BlockConstr | BlockConstr)
+ */
+ Tree constrDefOrSig(int mods) {
+ int pos = s.pos;
+ Name name = ident().toConstrName();
+ TypeDef[] tparams = typeParamClauseOpt();
+ ValDef[][] vparams = new ValDef[][]{paramClause()};
+ Tree restype = typedOpt();
+ if (s.token == LBRACE)
+ return make.DefDef(pos, mods, name, tparams, vparams,
+ restype, blockConstr());
+ else if (s.token == EQUALS || restype == Tree.Empty) {
+ accept(EQUALS);
+ return make.DefDef(pos, mods, name, tparams, vparams,
+ restype, (s.token == LBRACE) ? blockConstr() : constr());
+ } else
+ return make.DefDef(pos, mods | Modifiers.ABSTRACT, name,
+ tparams, vparams, restype, Tree.Empty);
+ }
+
+ /** TypeDef ::= Id [TypeParamClause] `=' Type
+ * TypeSig ::= Id [`<:' Type]
+ */
+ Tree typeDefOrSig(int mods) {
+ int pos = s.pos;
+ Name name = ident().toTypeName();
+ if (s.token == SUBTYPE) {
+ s.nextToken();
+ return make.TypeDef(pos, mods | Modifiers.ABSTRACT, name,
+ Tree.ExtTypeDef.EMPTY_ARRAY, type());
+ } else if (s.token == LBRACKET) {
+ TypeDef[] tparams = typeParamClauseOpt();
+ accept(EQUALS);
+ return make.TypeDef(pos, mods, name, tparams, type());
+ } else if (s.token == EQUALS) {
+ s.nextToken();
+ return make.TypeDef(pos, mods, name,
+ Tree.ExtTypeDef.EMPTY_ARRAY, type());
+ } else if (s.token == SEMI || s.token == COMMA) {
+ return make.TypeDef(
+ pos, mods | Modifiers.ABSTRACT, name,
+ Tree.ExtTypeDef.EMPTY_ARRAY, scalaAnyType(pos));
+ } else {
+ return syntaxError("`=' or `<:' expected", true);
+ }
+ }
+
+ /** ClassDef ::= Id [TypeParamClause] ParamClause [`:' Type] ClassTemplate
+ * TraitDef ::= Id [TypeParamClause] [`:' Type] ClassTemplate
+ */
+ Tree classDef(int mods) {
+ int pos = s.pos;
+ Name name = ident();
+ TypeDef[] tparams = typeParamClauseOpt();
+ ValDef[][] params;
+ if ((mods & Modifiers.TRAIT) == 0) params = new ValDef[][]{paramClause()};
+ else params = new ValDef[][]{};
+ return make.ClassDef(pos, mods, name.toTypeName(), tparams, params,
+ typedOpt(), classTemplate());
+ }
+
+ /** ModuleDef ::= Id [`:' Type] ClassTemplate
+ */
+ Tree moduleDef(int mods) {
+ return make.ModuleDef(
+ s.pos, mods, ident(), typedOpt(), classTemplate());
+ }
+
+ /** ClassTemplate ::= extends Template
+ * | TemplateBody
+ */
+ Template classTemplate() {
+ int pos = s.pos;
+ if (s.token == EXTENDS) {
+ s.nextToken();
+ return template();
+ } else if (s.token == LBRACE) {
+ return (Template)make.Template(
+ pos, new Tree[]{scalaObjectConstr(pos)}, templateBody());
+ } else {
+ syntaxError("`extends' or `{' expected", true);
+ return (Template)make.Template(
+ pos, new Tree[]{scalaObjectConstr(pos)}, Tree.EMPTY_ARRAY);
+ }
+ }
+
+////////// TEMPLATES ////////////////////////////////////////////////////////////
+
+
+ /** Template ::= Constr {`with' Constr} [TemplateBody]
+ */
+ Template template() {
+ int pos = s.pos;
+ TreeList parents = new TreeList();
+ parents.append(constr());
+ while (s.token == WITH) {
+ s.nextToken();
+ if (s.token == LBRACE)
+ return (Template)make.Template(pos, parents.toArray(), templateBody());
+ else
+ parents.append(constr());
+ }
+ Tree[] stats = (s.token == LBRACE) ? templateBody() : Tree.EMPTY_ARRAY;
+ return (Template)make.Template(pos, parents.toArray(), stats);
+ }
+
+ /** Constr ::= StableId [TypeArgs] [`(' [Exprs] `)']
+ */
+ Tree constr() {
+ Tree t = convertToConstr(stableId());
+ if (s.token == LBRACKET)
+ t = make.TypeApply(s.pos, t, typeArgs());
+ if (s.token == LPAREN)
+ t = make.Apply(s.pos, t, argumentExprs());
+ return t;
+ }
+
+ /** TemplateBody ::= `{' [TemplateStat {`;' TemplateStat}] `}'
+ */
+ Tree[] templateBody() {
+ accept(LBRACE);
+ Tree[] body = templateStatSeq();
+ accept(RBRACE);
+ return body;
+ }
+
+ /** Refinement ::= `{' [RefineStat {`;' RefineStat}] `}'
+ */
+ Tree[] refinement() {
+ accept(LBRACE);
+ Tree[] body = refineStatSeq();
+ accept(RBRACE);
+ return body;
+ }
+
+/////// STATSEQS //////////////////////////////////////////////////////////////
+
+ /** Packaging ::= package QualId `{' TopStatSeq `}'
+ */
+ Tree packaging() {
+ int pos = accept(PACKAGE);
+ Tree pkg = qualId();
+ accept(LBRACE);
+ Tree[] stats = topStatSeq();
+ accept(RBRACE);
+ return
+ make.PackageDef(pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, stats));
+ }
+
+ /** TopStatSeq ::= [TopStat {`;' TopStat}]
+ * TopStat ::= Modifiers TopDef
+ * | Packaging
+ * | Import
+ * |
+ */
+ Tree[] topStatSeq() {
+ TreeList stats = new TreeList();
+ while (s.token != RBRACE && s.token != EOF) {
+ if (s.token == PACKAGE) {
+ stats.append(packaging());
+ } else if (s.token == IMPORT) {
+ stats.append(importClause());
+ } else if (s.token == CLASS ||
+ s.token == CASECLASS ||
+ s.token == TRAIT ||
+ s.token == MODULE ||
+ isModifier()) {
+ stats.append(topDef(modifiers()));
+ } else if (s.token != SEMI) {
+ syntaxError("illegal start of class or module definition", true);
+ }
+ if (s.token != RBRACE && s.token != EOF) accept(SEMI);
+ }
+ return stats.toArray();
+ }
+
+ /** TemplateStatSeq ::= TemplateStat {`;' TemplateStat}
+ * TemplateStat ::= Import
+ * | Modifiers Def
+ * | Modifiers Dcl
+ * | Expr
+ * % | val this `:' Type
+ * |
+ */
+ Tree[] templateStatSeq() {
+ TreeList stats = new TreeList();
+ while (s.token != RBRACE && s.token != EOF) {
+ if (s.token == IMPORT) {
+ stats.append(importClause());
+ } else if (isExprIntro()) {
+ stats.append(expr());
+ } else if (isDefIntro() || isModifier()) {
+ stats.append(defOrDcl(modifiers()));
+ } else if (s.token != SEMI) {
+ syntaxError("illegal start of definition", true);
+ }
+ if (s.token != RBRACE) accept(SEMI);
+ }
+ return stats.toArray();
+ }
+
+ /** RefineStatSeq ::= RefineStat {`;' RefineStat}
+ * RefineStat ::= Dcl
+ * | type TypeDef {`,' TypeDef}
+ * |
+ */
+ Tree[] refineStatSeq() {
+ TreeList stats = new TreeList();
+ while (s.token != RBRACE && s.token != EOF) {
+ if (isDclIntro()) {
+ stats.append(defOrDcl(0));
+ } else if (s.token != SEMI) {
+ syntaxError("illegal start of declaration", true);
+ }
+ if (s.token != RBRACE) accept(SEMI);
+ }
+ return stats.toArray();
+ }
+
+ /** BlockStatSeq ::= { BlockStat `;' } [Expr]
+ * BlockStat ::= Import
+ * | Def
+ * | LocalClassModifiers LocalTopDef
+ * | Expr
+ * |
+ */
+ Tree[] blockStatSeq(TreeList stats) {
+ while ((s.token != RBRACE) && (s.token != EOF) && (s.token != CASE)) {
+ if (s.token == IMPORT) {
+ stats.append(importClause());
+ accept(SEMI);
+ } else if (isExprIntro()) {
+ stats.append(expr());
+ if (s.token != RBRACE && s.token != CASE) accept(SEMI);
+ } else if (isDefIntro()) {
+ stats.append(defOrDcl(0));
+ accept(SEMI);
+ } else if (isLocalClassModifier()) {
+ stats.append(topDef(localClassModifiers()));
+ accept(SEMI);
+ } else if (s.token == SEMI) {
+ s.nextToken();
+ } else {
+ syntaxError("illegal start of statement", true);
+ }
+ }
+ return stats.toArray();
+ }
+
+
+ /** CompilationUnit = [package QualId `;'] TopStatSeq
+ */
+ Tree[] compilationUnit() {
+ if (s.token == PACKAGE) {
+ int pos = s.skipToken();
+ Tree pkg = qualId();
+ if (s.token == SEMI) {
+ s.nextToken();
+ return new Tree[]{
+ make.PackageDef(
+ pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, topStatSeq()))};
+ } else {
+ TreeList stats = new TreeList();
+ accept(LBRACE);
+ stats.append(
+ make.PackageDef(
+ pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, topStatSeq())));
+ accept(RBRACE);
+ stats.append(topStatSeq());
+ return stats.toArray();
+ }
+ } else {
+ return topStatSeq();
+ }
+ }
+}
+
diff --git a/sources/scalac/ast/parser/ParserPhase.java b/sources/scalac/ast/parser/ParserPhase.java
new file mode 100644
index 0000000000..204fb2b925
--- /dev/null
+++ b/sources/scalac/ast/parser/ParserPhase.java
@@ -0,0 +1,63 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+package scalac.ast.parser;
+
+import java.io.*;
+import scalac.*;
+
+public class ParserPhase extends PhaseDescriptor {
+
+ public String name() {
+ return "parse";
+ }
+
+ public String description () {
+ return "parse source files";
+ }
+
+ public String taskDescription() {
+ return "parsed";
+ }
+
+ public Phase createPhase(Global global) {
+ return new ParserWorker(global, this);
+ }
+}
+
+public class ParserWorker extends Phase {
+
+ /** constructor
+ */
+ public ParserWorker(Global global, PhaseDescriptor descr) {
+ super(global, descr);
+ }
+
+ /** apply this phase to all compilation units
+ */
+ public void apply() {
+ super.apply();
+ int count = 0;
+ for (int i = 0; i < global.units.length; i++) {
+ if (global.units[i].body != null) count++;
+ }
+ Unit[] units = new Unit[count];
+ for (int i = 0, j = 0; i < global.units.length; i++) {
+ if (global.units[i].body != null) units[j++] = global.units[i];
+ }
+ global.units = units;
+ }
+
+ /** apply this phase to the given compilation unit
+ */
+ public void apply(Unit unit) {
+ global.start();
+ unit.body = new Parser(unit).parse();
+ global.stop("parsed " + unit.source);
+ }
+}
diff --git a/sources/scalac/ast/parser/Scanner.java b/sources/scalac/ast/parser/Scanner.java
new file mode 100644
index 0000000000..0e4af3a09d
--- /dev/null
+++ b/sources/scalac/ast/parser/Scanner.java
@@ -0,0 +1,793 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast.parser;
+
+import scalac.*;
+import scalac.util.Name;
+import scalac.util.Position;
+
+/** A scanner for the programming language Scala.
+ *
+ * @author Matthias Zenger, Martin Odersky
+ * @version 1.0
+ */
+public class Scanner extends TokenData {
+
+ /** layout & character constants
+ */
+ public int tabinc = 8;
+ public final static byte LF = 0xA;
+ protected final static byte FF = 0xC;
+ protected final static byte CR = 0xD;
+ protected final static byte SU = Sourcefile.SU;
+
+ /** the names of all tokens
+ */
+ public Name[] tokenName = new Name[128];
+ public int numToken = 0;
+
+ /** keyword array; maps from name indices to tokens
+ */
+ protected byte[] key;
+ protected int maxKey = 0;
+
+ /** we need one token lookahead
+ */
+ protected TokenData next = new TokenData();
+ protected TokenData prev = new TokenData();
+
+ /** the first character position after the previous token
+ */
+ public int lastpos = 0;
+
+ /** the last error position
+ */
+ public int errpos = -1;
+
+ /** the input buffer:
+ */
+ protected byte[] buf;
+ protected int bp;
+
+ /** the current character
+ */
+ protected byte ch;
+
+ /** the line and column position of the current character
+ */
+ public int cline;
+ public int ccol;
+
+ /** the current sourcefile
+ */
+ public Sourcefile currentSource;
+
+ /** a buffer for character and string literals
+ */
+ protected byte[] lit = new byte[64];
+ protected int litlen;
+
+ /** the compilation unit
+ */
+ public Unit unit;
+
+
+ /** Construct a scanner from a file input stream.
+ */
+ public Scanner(Unit unit) {
+ this.unit = unit;
+ buf = (currentSource = unit.source).getBuffer();
+ cline = 1;
+ bp = -1;
+ ccol = 0;
+ nextch();
+ token = EMPTY;
+ init();
+ nextToken();
+ }
+
+ private void nextch() {
+ ch = buf[++bp]; ccol++;
+ }
+
+ /** read next token and return last position
+ */
+ public int skipToken() {
+ int p = pos;
+ nextToken();
+ return p;
+ }
+
+ public void nextToken() {
+ if (token == RBRACE) {
+ int prevpos = pos;
+ fetchToken();
+ switch (token) {
+ case ELSE: case EXTENDS: case WITH:
+ case YIELD: case DO:
+ case COMMA: case SEMI: case DOT:
+ case COLON: case EQUALS: case ARROW:
+ case LARROW: case SUBTYPE: case AT:
+ case HASH: case AS: case IS:
+ case RPAREN: case RBRACKET: case RBRACE:
+ break;
+ default:
+ if (token == EOF ||
+ ((pos >>> Position.LINESHIFT) >
+ (prevpos >>> Position.LINESHIFT))) {
+ next.copyFrom(this);
+ this.token = SEMI;
+ this.pos = prevpos;
+ }
+ }
+ } else {
+ if (next.token == EMPTY) {
+ fetchToken();
+ } else {
+ copyFrom(next);
+ next.token = EMPTY;
+ }
+ if (token == CASE) {
+ prev.copyFrom(this);
+ fetchToken();
+ if (token == CLASS) {
+ token = CASECLASS;
+ } else {
+ next.copyFrom(this);
+ this.copyFrom(prev);
+ }
+ } else if (token == SEMI) {
+ prev.copyFrom(this);
+ fetchToken();
+ if (token != ELSE) {
+ next.copyFrom(this);
+ this.copyFrom(prev);
+ }
+ }
+ }
+ //System.out.println("<" + token2string(token) + ">");//DEBUG
+ }
+
+ /** read next token
+ */
+ public void fetchToken() {
+ if (token == EOF) return;
+ lastpos = Position.encode(cline, ccol, currentSource.id);
+ int index = bp;
+ while(true) {
+ switch (ch) {
+ case ' ':
+ nextch();
+ break;
+ case '\t':
+ ccol = ((ccol - 1) / tabinc * tabinc) + tabinc;
+ nextch();
+ break;
+ case CR:
+ cline++;
+ ccol = 0;
+ nextch();
+ if (ch == LF) {
+ ccol = 0;
+ nextch();
+ }
+ break;
+ case LF:
+ case FF:
+ cline++;
+ ccol = 0;
+ nextch();
+ break;
+ default:
+ pos = Position.encode(cline, ccol, currentSource.id);
+ index = bp;
+ switch (ch) {
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z': case '$':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ nextch();
+ getIdentRest(index);
+ return;
+ case '~': case '!': case '@': case '#': case '%':
+ case '^': case '*': case '+': case '-': case '<':
+ case '>': case '?': case ':':
+ case '=': case '&': case '|':
+ nextch();
+ getOperatorRest(index);
+ return;
+ case '/':
+ nextch();
+ if (!skipComment()) {
+ getOperatorRest(index);
+ return;
+ }
+ break;
+ case '_':
+ nextch();
+ getIdentOrOperatorRest(index);
+ return;
+ case '0':
+ nextch();
+ if (ch == 'x' || ch == 'X') {
+ nextch();
+ getNumber(index + 2, 16);
+ } else
+ getNumber(index, 8);
+ return;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ getNumber(index, 10);
+ return;
+ case '\"':
+ nextch();
+ litlen = 0;
+ while (ch != '\"' && ch != CR && ch != LF && ch != SU)
+ getlitch();
+ if (ch == '\"') {
+ token = STRINGLIT;
+ name = Name.fromSource(lit, 0, litlen);
+ nextch();
+ }
+ else
+ syntaxError("unclosed character literal");
+ return;
+ case '\'':
+ nextch();
+ litlen = 0;
+ getlitch();
+ if (ch == '\'') {
+ nextch();
+ token = CHARLIT;
+ byte[] ascii = new byte[litlen * 2];
+ int alen = SourceRepresentation.source2ascii(lit, 0, litlen, ascii);
+ if (alen > 0)
+ intVal = SourceRepresentation.ascii2string(ascii, 0, alen).charAt(0);
+ else
+ intVal = 0;
+ } else
+ syntaxError("unclosed character literal");
+ return;
+ case '.':
+ nextch();
+ if (('0' <= ch) && (ch <= '9')) getFraction(index);
+ else token = DOT;
+ return;
+ case ';':
+ nextch(); token = SEMI;
+ return;
+ case ',':
+ nextch(); token = COMMA;
+ return;
+ case '(':
+ nextch(); token = LPAREN;
+ return;
+ case '{':
+ nextch(); token = LBRACE;
+ return;
+ case ')':
+ nextch(); token = RPAREN;
+ return;
+ case '}':
+ nextch(); token = RBRACE;
+ return;
+ case '[':
+ nextch(); token = LBRACKET;
+ return;
+ case ']':
+ nextch(); token = RBRACKET;
+ return;
+ case SU:
+ token = EOF;
+ currentSource.lines = cline;
+ return;
+ default:
+ nextch();
+ syntaxError("illegal character");
+ return;
+ }
+ }
+ }
+ }
+
+ private boolean skipComment() {
+ if (ch == '/') {
+ do {
+ nextch();
+ } while ((ch != CR) && (ch != LF) && (ch != SU));
+ return true;
+ } else if (ch == '*') {
+ int openComments = 1;
+ while (openComments > 0) {
+ do {
+ do {
+ if (ch == CR) {
+ cline++;
+ ccol = 0;
+ nextch();
+ if (ch == LF) {
+ ccol = 0;
+ nextch();
+ }
+ } else if (ch == LF) {
+ cline++;
+ ccol = 0;
+ nextch();
+ }
+ else if (ch == '\t') {
+ ccol = ((ccol - 1) / tabinc * tabinc) + tabinc;
+ nextch();
+ } else if (ch == '/') {
+ nextch();
+ if (ch == '*') {
+ nextch();
+ openComments++;
+ }
+ } else {
+ nextch();
+ }
+ } while ((ch != '*') && (ch != SU));
+ while (ch == '*') {
+ nextch();
+ }
+ } while (ch != '/' && ch != SU);
+ if (ch == '/') {
+ nextch();
+ openComments--;
+ } else {
+ syntaxError("unclosed comment");
+ return true;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void getIdentRest(int index) {
+ while (true) {
+ switch (ch) {
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z': case '$':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ nextch();
+ break;
+ case '_':
+ nextch();
+ getIdentOrOperatorRest(index);
+ return;
+ default:
+ treatIdent(index, bp);
+ return;
+ }
+ }
+ }
+
+ private void getOperatorRest(int index) {
+ while (true) {
+ switch (ch) {
+ case '~': case '!': case '@': case '#': case '%':
+ case '^': case '*': case '+': case '-': case '<':
+ case '>': case '?': case ':':
+ case '=': case '&': case '|':
+ nextch();
+ break;
+ case '/':
+ int lastbp = bp;
+ nextch();
+ if (skipComment()) {
+ treatIdent(index, lastbp);
+ return;
+ } else {
+ break;
+ }
+ case '_':
+ nextch();
+ getIdentOrOperatorRest(index);
+ return;
+ default:
+ treatIdent(index, bp);
+ return;
+ }
+ }
+ }
+
+ private void getIdentOrOperatorRest(int index) {
+ switch (ch) {
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z': case '$':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ getIdentRest(index);
+ return;
+ case '~': case '!': case '@': case '#': case '%':
+ case '^': case '*': case '+': case '-': case '<':
+ case '>': case '?': case ':':
+ case '=': case '&': case '|':
+ case '/':
+ getOperatorRest(index);
+ return;
+ case '_':
+ nextch();
+ getIdentOrOperatorRest(index);
+ return;
+ default:
+ treatIdent(index, bp);
+ return;
+ }
+ }
+
+ void treatIdent(int start, int end) {
+ name = Name.fromAscii(buf, start, end - start);
+ if (name.index <= maxKey)
+ token = key[name.index];
+ else
+ token = IDENTIFIER;
+ }
+
+ /** generate an error at the given position
+ */
+ void syntaxError(int pos, String msg) {
+ unit.error(pos, msg);
+ token = ERROR;
+ errpos = pos;
+ }
+
+ /** generate an error at the current token position
+ */
+ void syntaxError(String msg) {
+ syntaxError(pos, msg);
+ }
+
+ /** append characteter to "lit" buffer
+ */
+ protected void putch(byte c) {
+ if (litlen == lit.length) {
+ byte[] newlit = new byte[lit.length * 2];
+ System.arraycopy(lit, 0, newlit, 0, lit.length);
+ lit = newlit;
+ }
+ lit[litlen++] = c;
+ }
+
+ /** return true iff next 6 characters are a valid unicode sequence:
+ */
+ protected boolean isUnicode() {
+ return
+ (bp + 6) < buf.length &&
+ (buf[bp] == '\\') &&
+ (buf[bp+1] == 'u') &&
+ (SourceRepresentation.digit2int(buf[bp+2], 16) >= 0) &&
+ (SourceRepresentation.digit2int(buf[bp+3], 16) >= 0) &&
+ (SourceRepresentation.digit2int(buf[bp+4], 16) >= 0) &&
+ (SourceRepresentation.digit2int(buf[bp+5], 16) >= 0);
+ }
+
+ /** read next character in character or string literal:
+ */
+ protected void getlitch() {
+ if (ch == '\\') {
+ if (isUnicode()) {
+ putch(ch); nextch();
+ putch(ch); nextch();
+ putch(ch); nextch();
+ putch(ch); nextch();
+ putch(ch); nextch();
+ putch(ch); nextch();
+ } else {
+ nextch();
+ if ('0' <= ch && ch <= '7') {
+ byte leadch = ch;
+ int oct = SourceRepresentation.digit2int(ch, 8);
+ nextch();
+ if ('0' <= ch && ch <= '7') {
+ oct = oct * 8 + SourceRepresentation.digit2int(ch, 8);
+ nextch();
+ if (leadch <= '3' && '0' <= ch && ch <= '7') {
+ oct = oct * 8 + SourceRepresentation.digit2int(ch, 8);
+ nextch();
+ }
+ }
+ putch((byte)oct);
+ } else if (ch != SU) {
+ switch (ch) {
+ case 'b': case 't': case 'n':
+ case 'f': case 'r': case '\"':
+ case '\'': case '\\':
+ putch((byte)'\\');
+ putch(ch);
+ break;
+ default:
+ syntaxError(Position.encode(cline, ccol, currentSource.id) - 1, "invalid escape character");
+ putch(ch);
+ }
+ nextch();
+ }
+ }
+ } else if (ch != SU) {
+ putch(ch);
+ nextch();
+ }
+ }
+
+ /** read fractional part of floating point number;
+ * Then floatVal := buf[index..], converted to a floating point number.
+ */
+ protected void getFraction(int index) {
+ while (SourceRepresentation.digit2int(ch, 10) >= 0) {
+ nextch();
+ }
+ token = DOUBLELIT;
+ if ((ch == 'e') || (ch == 'E')) {
+ nextch();
+ if ((ch == '+') || (ch == '-')) {
+ byte sign = ch;
+ nextch();
+ if (('0' > ch) || (ch > '9')) {
+ ch = sign;
+ bp--;
+ ccol--;
+ }
+ }
+ while (SourceRepresentation.digit2int(ch, 10) >= 0) {
+ nextch();
+ }
+ }
+ double limit = Double.MAX_VALUE;
+ if ((ch == 'd') || (ch == 'D')) {
+ nextch();
+ } else if ((ch == 'f') || (ch == 'F')) {
+ token = FLOATLIT;
+ limit = Float.MAX_VALUE;
+ nextch();
+ }
+ try {
+ floatVal = Double.valueOf(new String(buf, index, bp - index)).doubleValue();
+ if (floatVal > limit)
+ syntaxError("floating point number too large");
+ } catch (NumberFormatException e) {
+ syntaxError("malformed floating point number");
+ }
+ }
+
+ /** intVal := buf[index..index+len-1], converted to an integer number.
+ * base = the base of the number; one of 8, 10, 16.
+ * max = the maximal number before an overflow.
+ */
+ protected void makeInt (int index, int len, int base, long max) {
+ intVal = 0;
+ int divider = (base == 10 ? 1 : 2);
+ for (int i = 0; i < len; i++) {
+ int d = SourceRepresentation.digit2int(buf[index + i], base);
+ if (d < 0) {
+ syntaxError("malformed integer number");
+ return;
+ }
+ if (intVal < 0 ||
+ max / (base / divider) < intVal ||
+ max - (d / divider) < (intVal * (base / divider) - 0)) {
+ syntaxError("integer number too large");
+ return;
+ }
+ intVal = intVal * base + d;
+ }
+ }
+
+ /** read a number,
+ * and convert buf[index..], setting either intVal or floatVal.
+ * base = the base of the number; one of 8, 10, 16.
+ */
+ protected void getNumber(int index, int base) {
+ while (SourceRepresentation.digit2int(ch, base == 8 ? 10 : base) >= 0) {
+ nextch();
+ }
+ if (base <= 10 && ch == '.') {
+ nextch();
+ if ((ch >= '0') && (ch <= '9'))
+ getFraction(index);
+ else {
+ ch = buf[--bp]; ccol--;
+ makeInt(index, bp - index, base, Integer.MAX_VALUE);
+ intVal = (int)intVal;
+ token = INTLIT;
+ }
+ } else if (base <= 10 &&
+ (ch == 'e' || ch == 'E' ||
+ ch == 'f' || ch == 'F' ||
+ ch == 'd' || ch == 'D'))
+ getFraction(index);
+ else {
+ if (ch == 'l' || ch == 'L') {
+ makeInt(index, bp - index, base, Long.MAX_VALUE);
+ nextch();
+ token = LONGLIT;
+ } else {
+ makeInt(index, bp - index, base, Integer.MAX_VALUE);
+ intVal = (int)intVal;
+ token = INTLIT;
+ }
+ }
+ }
+
+ public int name2token(Name name) {
+ if (name.index <= maxKey)
+ return key[name.index];
+ else
+ return IDENTIFIER;
+ }
+
+ public String token2string(int token) {
+ switch (token) {
+ case IDENTIFIER:
+ return "identifier";
+ case CHARLIT:
+ return "character literal";
+ case INTLIT:
+ return "integer literal";
+ case LONGLIT:
+ return "long literal";
+ case FLOATLIT:
+ return "float literal";
+ case DOUBLELIT:
+ return "double literal";
+ case STRINGLIT:
+ return "string literal";
+ case LPAREN:
+ return "'('";
+ case RPAREN:
+ return "')'";
+ case LBRACE:
+ return "'{'";
+ case RBRACE:
+ return "'}'";
+ case LBRACKET:
+ return "'['";
+ case RBRACKET:
+ return "']'";
+ case EOF:
+ return "eof";
+ case ERROR:
+ return "something";
+ case SEMI:
+ return "';'";
+ case COMMA:
+ return "','";
+ default:
+ try {
+ return "'" + tokenName[token].toString() + "'";
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return "'<" + token + ">'";
+ }
+ }
+ }
+
+ public String toString() {
+ switch (token) {
+ case IDENTIFIER:
+ return "id(" + name + ")";
+ case CHARLIT:
+ return "char(" + intVal + ")";
+ case INTLIT:
+ return "int(" + intVal + ")";
+ case LONGLIT:
+ return "long(" + intVal + ")";
+ case FLOATLIT:
+ return "float(" + floatVal + ")";
+ case DOUBLELIT:
+ return "double(" + floatVal + ")";
+ case STRINGLIT:
+ return "string(" + name + ")";
+ case SEMI:
+ return ";";
+ case COMMA:
+ return ",";
+ default:
+ return token2string(token);
+ }
+ }
+
+ protected void enterKeyword(String s, int tokenId) {
+ while (tokenId > tokenName.length) {
+ Name[] newTokName = new Name[tokenName.length * 2];
+ System.arraycopy(tokenName, 0, newTokName, 0, newTokName.length);
+ tokenName = newTokName;
+ }
+ Name n = Name.fromString(s);
+ tokenName[tokenId] = n;
+ if (n.index > maxKey)
+ maxKey = n.index;
+ if (tokenId >= numToken)
+ numToken = tokenId + 1;
+ }
+
+ protected void init() {
+ initKeywords();
+ key = new byte[maxKey+1];
+ for (int i = 0; i <= maxKey; i++)
+ key[i] = IDENTIFIER;
+ for (byte j = 0; j < numToken; j++)
+ if (tokenName[j] != null)
+ key[tokenName[j].index] = j;
+ }
+
+ protected void initKeywords() {
+ enterKeyword("if", IF);
+ enterKeyword("for", FOR);
+ enterKeyword("else", ELSE);
+ enterKeyword("this", THIS);
+ enterKeyword("null", NULL);
+ enterKeyword("new", NEW);
+ enterKeyword("with", WITH);
+ enterKeyword("super", SUPER);
+ enterKeyword("case", CASE);
+ enterKeyword("val", VAL);
+ enterKeyword("abstract", ABSTRACT);
+ enterKeyword("final", FINAL);
+ enterKeyword("private", PRIVATE);
+ enterKeyword("protected", PROTECTED);
+ enterKeyword("qualified", QUALIFIED);
+ enterKeyword("override", OVERRIDE);
+ enterKeyword("var", VAR);
+ enterKeyword("def", DEF);
+ enterKeyword("type", TYPE);
+ enterKeyword("extends", EXTENDS);
+ enterKeyword("let", LET);
+ enterKeyword("module", MODULE);
+ enterKeyword("class",CLASS);
+ enterKeyword("constr",CONSTR);
+ enterKeyword("import", IMPORT);
+ enterKeyword("package", PACKAGE);
+ enterKeyword(".", DOT);
+ enterKeyword("_", USCORE);
+ enterKeyword(":", COLON);
+ enterKeyword("=", EQUALS);
+ enterKeyword("=>", ARROW);
+ enterKeyword("<-", LARROW);
+ enterKeyword("<:", SUBTYPE);
+ enterKeyword("yield", YIELD);
+ enterKeyword("do", DO);
+ enterKeyword("@", AT);
+ enterKeyword("#", HASH);
+ enterKeyword("trait", TRAIT);
+ enterKeyword("as", AS);
+ enterKeyword("is", IS);
+ }
+}
+
+
diff --git a/sources/scalac/ast/parser/SourceRepresentation.java b/sources/scalac/ast/parser/SourceRepresentation.java
new file mode 100644
index 0000000000..2a20e13fab
--- /dev/null
+++ b/sources/scalac/ast/parser/SourceRepresentation.java
@@ -0,0 +1,205 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id
+\* */
+
+package scalac.ast.parser;
+
+public final class SourceRepresentation {
+
+ public static int digit2int(byte ch, int base) {
+ if ('0' <= ch && ch <= '9' && ch < '0' + base)
+ return ch - '0';
+ else if ('A' <= ch && ch < 'A' + base - 10)
+ return ch - 'A' + 10;
+ else if ('a' <= ch && ch < 'a' + base - 10)
+ return ch - 'a' + 10;
+ else
+ return -1;
+ }
+
+ public static byte int2digit(int x) {
+ if (x <= 9)
+ return (byte)(x + '0');
+ else
+ return (byte)(x - 10 + 'A');
+ }
+
+/* the next 4 functions convert between three fundamental name
+ * representations:
+ * - string each character 16 bit,
+ * - source characters outside 0..127 are represented by
+ * unicode escapes, \ u X X X X
+ * - ascii characters outside 0..127 are represented by two or three
+ * byte sequences with high bit set (as in class file format).
+ */
+
+/** convert source bytes in source[offset..offset+len-1] to ascii.
+ */
+ public static int source2ascii(byte source[], int offset, int len, byte ascii[]) {
+ int j = 0;
+ int i = 0;
+ while (i < len) {
+ if (source[offset + i] == '\\' && i + 1 < len) {
+ i++;
+ switch (source[offset + i]) {
+ case 'n':
+ ascii[j++] = (byte)'\n'; i++; continue;
+ case 't':
+ ascii[j++] = (byte)'\t'; i++; continue;
+ case 'b':
+ ascii[j++] = (byte)'\b'; i++; continue;
+ case 'r':
+ ascii[j++] = (byte)'\r'; i++; continue;
+ case 'f':
+ ascii[j++] = (byte)'\f'; i++; continue;
+ case 'u':
+ if (i + 4 < len) {
+ int code = 0;
+ int k = 1;
+ int d = 0;
+ while (k <= 4 && d >= 0) {
+ d = digit2int(source[offset + i + k], 16);
+ code = code * 16 + d;
+ k++;
+ }
+ if (d >= 0) {
+ if (code <= 0x7F)
+ ascii[j++] = (byte)code;
+ else
+ if (code <= 0x3FF) {
+ ascii[j++] = (byte)(0xC0 | (code >> 6));
+ ascii[j++] = (byte)(0x80 | (code & 0x3F));
+ } else {
+ ascii[j++] = (byte)(0xE0 | (code >> 12));
+ ascii[j++] = (byte)(0x80 |
+ ((code >> 6) & 0x3F));
+ ascii[j++] = (byte)(0x80 | (code & 0x3F));
+ }
+ i = i + 5;
+ continue;
+ }
+ }
+ }
+ }
+ byte b = source[offset + i++];
+ if (b >= 0)
+ ascii[j++] = b;
+ else {
+ ascii[j++] = (byte)(0xC0 | ((b >> 6) & 0x3));
+ ascii[j++] = (byte)(0x80 | (b & 0x3F));
+ }
+ }
+ return j;
+ }
+
+/** convert ascii bytes in ascii[offset..offset+len-1] to a string
+ */
+ public static String ascii2string(byte ascii[], int offset, int len) {
+ char cs[] = new char[len];
+ int i = offset;
+ int j = 0;
+ len += offset;
+ while (i < len) {
+ int b = ascii[i++] & 0xFF;
+ if (b >= 0xE0) {
+ b = ((b & 0x0F) << 12) | (ascii[i++] & 0x3F) << 6;
+ b = b | (ascii[i++] & 0x3F);
+ }
+ else
+ if (b >= 0xC0)
+ b = ((b & 0x1F) << 6) | (ascii[i++] & 0x3F);
+ cs[j++] = (char)b;
+ }
+ return new String(cs, 0, j);
+ }
+
+/** convert string to array of source bytes
+ */
+ public static byte[] string2source(String s) {
+ byte[] source = new byte[s.length() * 6];
+ int j = 0;
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ switch (ch) {
+ case '\n':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'n';
+ break;
+ case '\t':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'t';
+ break;
+ case '\b':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'b';
+ break;
+ case '\r':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'r';
+ break;
+ case '\f':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'f';
+ break;
+ case '\"':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'\"';
+ break;
+ case '\'':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'\'';
+ break;
+ case '\\':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'\\';
+ break;
+ default:
+ if ((' ' <= ch) && (ch <= 127))
+ source[j++] = (byte)ch;
+ else {
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'u';
+ source[j++] = int2digit((ch >> 12) & 0xF);
+ source[j++] = int2digit((ch >> 8) & 0xF);
+ source[j++] = int2digit((ch >> 4) & 0xF);
+ source[j++] = int2digit(ch & 0xF);
+ }
+ }
+ }
+ byte[] res = new byte[j];
+ System.arraycopy(source, 0, res, 0, j);
+ return res;
+ }
+
+/** convert string to array of ascii bytes
+ */
+ public static byte[] string2ascii(String s) {
+ byte[] source = string2source(s);
+ byte[] ascii = new byte[source.length * 2];
+ int alen = source2ascii(source, 0, source.length, ascii);
+ byte[] res = new byte[alen];
+ System.arraycopy(ascii, 0, res, 0, alen);
+ return res;
+ }
+
+/** escape all characters outside 32..127 in string s
+ */
+ public static String escape(String s) {
+ try {
+ return new String(string2source(s), "8859_1");
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new InternalError(e.getMessage());
+ }
+ }
+
+/** escape character c, if outside 32..127.
+ */
+ public static String escape(char c) {
+ char[] s = {c};
+ return escape(new String(s));
+ }
+}
diff --git a/sources/scalac/ast/parser/Sourcefile.java b/sources/scalac/ast/parser/Sourcefile.java
new file mode 100644
index 0000000000..91277f9ac8
--- /dev/null
+++ b/sources/scalac/ast/parser/Sourcefile.java
@@ -0,0 +1,304 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast.parser;
+
+import java.io.*;
+import java.util.Hashtable;
+import scalac.util.AbstractFile;
+import scalac.util.Name;
+import scalac.util.Position;
+
+
+/** This class represents a single scala source file. It provides
+ * functionality to read the file and to output error messages on
+ * a given line and column. Error messages are logged to
+ * decouple the time where an error message is issued from the
+ * time where the error messages are displayed.
+ *
+ * @author Matthias Zenger
+ * @version 1.0
+ */
+public class Sourcefile {
+
+ /** the id management
+ */
+ public static int numIds = 1;
+ public static String[] files = new String[]{"console", null, null, null};
+ public static Sourcefile[] sources = new Sourcefile[]{null, null, null, null};
+ public int id;
+
+ /** the filename
+ */
+ public final boolean console;
+ protected String filename;
+ public String shortname;
+ public String pathname;
+
+ /** the encoding of the file
+ */
+ protected String encoding;
+
+ /** a log of all errors generated so far; used to avoid printing an
+ * error message more than once
+ */
+ protected Hashtable recorded = new Hashtable();
+
+ /** the buffer containing the file that is currently translated
+ */
+ protected byte[] buf = null;
+
+ /** the last error position
+ */
+ protected int lastLine = 0;
+ protected int lastPos = 0;
+ protected int lineEnd = 0;
+ protected int newPos = 0;
+
+ /** constants used for source parsing
+ */
+ final static byte LF = 0xA;
+ final static byte FF = 0xC;
+ final static byte CR = 0xD;
+ final static byte SU = 0x1A;
+
+ /** set col to NO_COLUMN, if the printLine method should not mark
+ * the column
+ */
+ final static int NO_COLUMN = -1;
+
+ /** number of lines and bytes (not used internally)
+ */
+ public int lines; // set externally
+ public int bytes;
+
+ /** prompt after error?
+ */
+ public boolean prompt;
+
+ /** constructors
+ */
+ public Sourcefile(String filename, boolean console) throws IOException, FileNotFoundException {
+ this.console = console;
+ if (filename == null) {
+ this.filename = "(sourcefile not available)";
+ this.shortname = "?";
+ this.pathname = "?";
+ buf = new byte[]{SU};
+ } else {
+ File f = new File(filename);
+ this.filename = filename;
+ this.shortname = f.getName();
+ this.pathname = f.getAbsoluteFile().getParentFile().
+ getCanonicalPath();
+ fillBuffer(new FileInputStream(f));
+ }
+ if (numIds == files.length) {
+ String[] newfiles = new String[numIds * 2];
+ System.arraycopy(files, 0, newfiles, 0, numIds);
+ files = newfiles;
+ Sourcefile[] newsources = new Sourcefile[numIds * 2];
+ System.arraycopy(sources, 0, newsources, 0, numIds);
+ sources = newsources;
+ }
+ sources[numIds] = this;
+ files[id = numIds++] = shortname;
+ }
+
+ public Sourcefile(AbstractFile abs) throws IOException, FileNotFoundException {
+ this.console = false;
+ if (filename == null) {
+ this.filename = "(sourcefile not available)";
+ this.shortname = "?";
+ this.pathname = "?";
+ buf = new byte[]{SU};
+ } else {
+ this.filename = abs.getPath();
+ this.shortname = abs.getName();
+ this.pathname = abs.getPath();
+ fillBuffer(abs.getInputStream());
+ }
+ if (numIds == files.length) {
+ String[] newfiles = new String[numIds * 2];
+ System.arraycopy(files, 0, newfiles, 0, numIds);
+ files = newfiles;
+ Sourcefile[] newsources = new Sourcefile[numIds * 2];
+ System.arraycopy(sources, 0, newsources, 0, numIds);
+ sources = newsources;
+ }
+ sources[numIds] = this;
+ files[id = numIds++] = shortname;
+ }
+
+ public Sourcefile(byte[] input, boolean console) {
+ this.console = console;
+ if (input == null) {
+ this.filename = "(sourcefile not available)";
+ this.shortname = "?";
+ this.pathname = "?";
+ buf = new byte[]{SU};
+ } else {
+ this.filename = "console";
+ this.shortname = "console";
+ this.pathname = "console";
+ buf = new byte[input.length + 2];
+ System.arraycopy(input, 0, buf, 0, input.length);
+ buf[input.length] = Scanner.LF;
+ buf[input.length + 1] = SU;
+ }
+ sources[0] = this;
+ id = 0;
+ }
+
+ /** fill the buffer using the InputStream
+ */
+ private void fillBuffer(InputStream in) throws IOException {
+ try {
+ buf = new byte[(bytes = in.available()) + 1];
+ if (in.read(buf) != (buf.length - 1))
+ throw new IOException();
+ in.close();
+ buf[buf.length - 1] = SU;
+ } catch (IOException e) {
+ throw new IOException("cannot read '" + filename + "'");
+ }
+ }
+
+ /** return filename as a string
+ */
+ public String toString() {
+ return filename;
+ }
+
+ /** return filename as a name
+ */
+ public Name getName() {
+ return Name.fromString(filename);
+ }
+
+ /** return the shortname without the suffix
+ */
+ public String getShortnameWithoutSuffix() {
+ int idx = shortname.lastIndexOf('.');
+ if (idx < 0)
+ return shortname;
+ else
+ return shortname.substring(0, idx);
+ }
+
+ /** return the source buffer of this file
+ */
+ public byte[] getBuffer() {
+ return buf;
+ }
+
+ /** number of logged entries
+ */
+ public int logged() {
+ return recorded.size();
+ }
+
+ /** is there already an entry at position 'pos'
+ */
+ public boolean isLogged(int pos) {
+ return (recorded.get(new Integer(pos)) != null);
+ }
+
+ /** enter entry into log table
+ */
+ public void log(int pos, String message) {
+ recorded.put(new Integer(pos), message);
+ }
+
+ /** set encoding of the file
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /** return true if there is an entry for this position,
+ * otherwise return false and enter message into log
+ */
+ public boolean testAndSetLog(int pos, String message) {
+ if (!isLogged(pos)) {
+ log(pos, message);
+ return false;
+ }
+ return true;
+ }
+
+ /** get error message with line from sourcefile
+ */
+ public String getMessage(int pos, String message) {
+ if (pos == Position.NOPOS)
+ return filename + ": " + message;
+ else {
+ int fileId = Position.file(pos);
+ String filename = files[fileId];
+ int line = Position.line(pos);
+ int col = Position.column(pos);
+ String main = filename + ":" + line + ": " + message;
+ if ((fileId > 0) &&
+ (fileId < numIds) &&
+ (sources[fileId] != null))
+ return main + '\n' + sources[fileId].getLine(line, col);
+ else
+ return main;
+ //else
+ // System.out.println("(source file not available anymore)");
+ }
+ }
+
+ /** get source line
+ */
+ public String getLine(int line, int col) {
+ int pos = 0;
+ if (lastLine > line)
+ lastLine = 0;
+ else
+ pos = newPos;
+ while ((pos < buf.length) && (lastLine < line))
+ {
+ lastPos = pos;
+ while ((pos < buf.length) && (buf[pos] != CR) &&
+ (buf[pos] != LF) && (buf[pos] != FF))
+ pos++;
+ lineEnd = pos;
+ if (pos < buf.length)
+ pos++;
+ if ((pos < buf.length) && (buf[pos-1] == CR) && (buf[pos] == LF))
+ pos++;
+ lastLine++;
+ }
+ newPos = pos;
+ try
+ {
+ String errline = (encoding != null) ?
+ new String(buf, lastPos, lineEnd - lastPos, encoding) :
+ new String(buf, lastPos, lineEnd - lastPos);
+ if (col != NO_COLUMN)
+ {
+ byte[] ptr = new byte[col];
+ for (int i = col - 2; i >= 0; i--)
+ ptr[i] = (byte)' ';
+ ptr[col - 1] = (byte)'^';
+ return errline + '\n' + new String(ptr);
+ } else
+ return errline;
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError(e.getMessage());
+ }
+ }
+
+ /** release all sourcefile objects
+ */
+ public static void flushSources() {
+ for (int i = 0; i < sources.length; i++)
+ sources[i] = null;
+ }
+}
diff --git a/sources/scalac/ast/parser/TokenData.java b/sources/scalac/ast/parser/TokenData.java
new file mode 100644
index 0000000000..d6bcac6c1c
--- /dev/null
+++ b/sources/scalac/ast/parser/TokenData.java
@@ -0,0 +1,44 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast.parser;
+
+import scalac.util.Name;
+
+/** A class for representing a token's data.
+ *
+ * @author Matthias Zenger
+ * @version 1.0
+ */
+public class TokenData implements Tokens {
+
+ /** the next token
+ */
+ public int token = EMPTY;
+
+ /** the token's position. pos = line << Position.LINESHIFT + col
+ */
+ public int pos = 0;
+
+ /** the name of an identifier or token
+ */
+ public Name name;
+
+ /** the value of a number
+ */
+ public long intVal;
+ public double floatVal;
+
+ public void copyFrom(TokenData td) {
+ this.token = td.token;
+ this.pos = td.pos;
+ this.name = td.name;
+ this.intVal = td.intVal;
+ this.floatVal = td.floatVal;
+ }
+}
diff --git a/sources/scalac/ast/parser/Tokens.java b/sources/scalac/ast/parser/Tokens.java
new file mode 100644
index 0000000000..6c3bef6a55
--- /dev/null
+++ b/sources/scalac/ast/parser/Tokens.java
@@ -0,0 +1,84 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.ast.parser;
+
+public interface Tokens {
+ byte EMPTY = -3,
+ UNDEF = -2,
+ ERROR = -1,
+ EOF = 0,
+
+ /* literals */
+ CHARLIT = 1,
+ INTLIT = 2,
+ LONGLIT = 3,
+ FLOATLIT = 4,
+ DOUBLELIT = 5,
+ STRINGLIT = 6,
+
+ /* identifier */
+ IDENTIFIER = 10,
+
+ /* keywords */
+ IF = 20,
+ FOR = 21,
+ ELSE = 22,
+ THIS = 23,
+ NULL = 24,
+ NEW = 25,
+ WITH = 26,
+ SUPER = 27,
+ CASE = 28,
+ CASECLASS = 29,
+ VAL = 30,
+ ABSTRACT = 31,
+ FINAL = 32,
+ PRIVATE = 33,
+ PROTECTED = 34,
+ QUALIFIED = 35,
+ OVERRIDE = 36,
+ VAR = 37,
+ DEF = 38,
+ TYPE = 39,
+ EXTENDS = 40,
+ LET = 41,
+ MODULE = 43,
+ CLASS = 44,
+ CONSTR = 45,
+ IMPORT = 46,
+ PACKAGE = 47,
+ AS = 48,
+ IS = 49,
+ YIELD = 50,
+ DO = 51,
+ TRAIT = 52,
+
+ /* special symbols */
+ COMMA = 61,
+ SEMI = 62,
+ DOT = 63,
+ USCORE = 64,
+ COLON = 65,
+ EQUALS = 66,
+ LARROW = 57,
+ ARROW = 68,
+ SUBTYPE = 69,
+ AT = 70,
+ HASH = 71,
+
+ /* parenthesis */
+ LPAREN = 90,
+ RPAREN = 91,
+ LBRACKET = 92,
+ RBRACKET = 93,
+ LBRACE = 94,
+ RBRACE = 95;
+}
+
+
diff --git a/sources/scalac/ast/printer/HTMLTreePrinter.java b/sources/scalac/ast/printer/HTMLTreePrinter.java
new file mode 100644
index 0000000000..8dbd34f2a0
--- /dev/null
+++ b/sources/scalac/ast/printer/HTMLTreePrinter.java
@@ -0,0 +1,173 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+\* */
+
+// $Id$
+
+package scalac.ast.printer;
+
+import scalac.Unit;
+import scalac.symtab.Symbol;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.Math;
+import java.util.HashMap;
+
+/**
+ * HTML pretty printer for Scala abstract syntax trees.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+
+public class HTMLTreePrinter extends TextTreePrinter {
+ protected int outSectionLevel = 1;
+ protected boolean started = false;
+
+ public HTMLTreePrinter(OutputStream stream) {
+ super(stream);
+ }
+
+ public void begin() {
+ assert !started;
+
+ super.begin();
+ out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
+ out.println("<html>");
+ out.println("<head>");
+ out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">");
+ out.println("<link rel=\"stylesheet\" href=\"scala.css\" type=\"text/css\">");
+ out.println("<title>Scala tree</title>");
+ out.println("</head>");
+ out.println("<body>");
+
+ started = true;
+ }
+
+ public void end() {
+ assert started;
+
+ out.println("</body>");
+ out.println("</html>");
+ super.end();
+
+ started = false;
+ }
+
+ public void beginSection(int level, String title) {
+ outSectionLevel = Math.min(level, 4);
+ beginSection1(outSectionLevel, title);
+ }
+
+ protected void beginSection1(int level, String title) {
+ if (level == 1)
+ out.println("<hr/>");
+ String tag = "h" + level;
+ startTag(tag);
+ print(Text.Simple(title));
+ endTag(tag);
+ }
+
+ protected void startTag(String tag) {
+ out.print('<'); out.print(tag); out.print('>');
+ }
+
+ protected void startTag(String tag, String attr1, String val1) {
+ out.print('<');
+ out.print(tag);
+ out.print(' ');
+ out.print(attr1);
+ out.print("=\"");
+ out.print(val1);
+ out.print("\">");
+ }
+
+ protected void endTag(String tag) {
+ out.print("</"); out.print(tag); out.print(">");
+ }
+
+ protected void startSpan(String cls) {
+ startTag("span", "class", cls);
+ }
+
+ protected void endSpan() {
+ endTag("span");
+ }
+
+ protected void printString(String str) {
+ StringBuffer buf = null;
+ int strLen = str.length();
+
+ for (int i = 0; i < strLen; ++i) {
+ String entity;
+ char c = str.charAt(i);
+ switch (c) {
+ case '<': entity = "lt"; break;
+ case '>': entity = "gt"; break;
+ case '&': entity = "amp"; break;
+ default: entity = null; break;
+ }
+ if (entity != null) {
+ out.print('&');
+ out.print(entity);
+ out.print(';');
+ } else
+ out.print(c);
+ }
+ }
+
+ protected static HashMap/*<Symbol,Integer>*/ symAnchors = new HashMap();
+ protected String symbolAnchor(Symbol sym, SymbolUsage usage) {
+ Integer anchorId = (Integer)symAnchors.get(sym);
+ if (anchorId == null) {
+ anchorId = new Integer(symAnchors.size());
+ symAnchors.put(sym, anchorId);
+ }
+ if (usage == SymbolUsage.Definition)
+ return anchorId.toString();
+ else
+ return "#" + anchorId.toString();
+ }
+
+ protected void print(Text text) {
+ switch (text) {
+ case Keyword(String name):
+ startSpan("kw");
+ printString(name);
+ endSpan();
+ break;
+ case Literal(String str):
+ startSpan("lit");
+ printString(str);
+ endSpan();
+ break;
+ case Identifier(Symbol symbol, String name, SymbolUsage usage):
+ boolean defined = (usage == SymbolUsage.Definition);
+ if (defined) startSpan("idDef");
+ if (symbol != null) {
+ String attr = (defined ? "name" : "href");
+ startTag("a", attr, symbolAnchor(symbol, usage));
+ }
+ printString(name);
+ if (symbol != null)
+ endTag("a");
+ if (defined) endSpan();
+ break;
+ default:
+ super.print(text);
+ }
+ }
+
+ protected void printUnitHeader(Unit unit) {
+ beginSection1(outSectionLevel + 1, unit.source.toString());
+ startTag("pre");
+ }
+
+ protected void printUnitFooter(Unit unit) {
+ endTag("pre");
+ }
+}
diff --git a/sources/scalac/ast/printer/TextTreePrinter.java b/sources/scalac/ast/printer/TextTreePrinter.java
new file mode 100644
index 0000000000..c5138d025f
--- /dev/null
+++ b/sources/scalac/ast/printer/TextTreePrinter.java
@@ -0,0 +1,683 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+\* */
+
+// $Id$
+
+package scalac.ast.printer;
+
+import scalac.ast.*;
+import scalac.symtab.*;
+import scalac.util.Debug;
+import scalac.Global;
+import scalac.Unit;
+import scalac.util.Name;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Text pretty printer for Scala abstract syntax trees.
+ *
+ * @author Michel Schinz, Matthias Zenger
+ * @version 1.0
+ */
+public class TextTreePrinter implements TreePrinter {
+ protected PrintWriter out;
+
+ protected int indent = 0;
+ protected final int INDENT_STEP = 2;
+ protected final String INDENT_STRING =
+ " ";
+ protected final int MAX_INDENT = INDENT_STRING.length();
+
+ public TextTreePrinter(OutputStream stream) {
+ out = new PrintWriter(stream);
+ }
+
+ public TextTreePrinter() {
+ this(System.out);
+ }
+
+ public void begin() { }
+
+ public void end() {
+ out.flush();
+ }
+
+ public void flush() {
+ out.flush();
+ }
+
+ public TreePrinter print(String str) {
+ out.print(str);
+ return this;
+ }
+
+ public TreePrinter println() {
+ out.println();
+ return this;
+ }
+
+ public void beginSection(int level, String title) {
+ out.println("[[" + title + "]]");
+ out.flush();
+ }
+
+ protected void indent() {
+ indent += Math.min(MAX_INDENT, INDENT_STEP);
+ }
+
+ protected void undent() {
+ indent -= Math.max(0, INDENT_STEP);
+ }
+
+ protected void printString(String str) {
+ out.print(str);
+ }
+
+ protected void printNewLine() {
+ out.println();
+ if (indent > 0)
+ out.write(INDENT_STRING, 0, indent);
+ }
+
+ public static class SymbolUsage {
+ public case Definition;
+ public case Use;
+ }
+
+ public static class Text {
+ public case None;
+ public case Space;
+ public case Newline;
+ public case Simple(String str);
+ public case Literal(String str);
+ public case Keyword(String name);
+ public case Identifier(Symbol symbol, String name, SymbolUsage usage);
+ public case Sequence(Text[] elements);
+ }
+
+ protected void print(Text text) {
+ switch (text) {
+ case None : break;
+ case Space : printString(" "); break;
+ case Newline : printNewLine(); break;
+ case Simple(String str) : printString(str); break;
+ case Literal(String str) : printString(str); break;
+ case Keyword(String name) : printString(name); break;
+ case Identifier(Symbol sym, String name, _) :
+ printString(name);
+ if (sym != null && Global.instance.uniqid)
+ printString("#" + Global.instance.uniqueID.id(sym));
+ break;
+ case Sequence(Text[] elements) : print(elements); break;
+ }
+ }
+
+ protected void print(Text[] texts) {
+ for (int i = 0; i < texts.length; ++i)
+ print(texts[i]);
+ }
+
+ protected static final Text KW_ABSTRACT = Text.Keyword("abstract");
+ protected static final Text KW_CASE = Text.Keyword("case");
+ protected static final Text KW_CLASS = Text.Keyword("class");
+ protected static final Text KW_CONSTR = Text.Keyword("constr");
+ protected static final Text KW_DEF = Text.Keyword("def");
+ protected static final Text KW_DO = Text.Keyword("do");
+ protected static final Text KW_ELSE = Text.Keyword("else");
+ protected static final Text KW_EXTENDS = Text.Keyword("extends");
+ protected static final Text KW_FINAL = Text.Keyword("final");
+ protected static final Text KW_FOR = Text.Keyword("for");
+ protected static final Text KW_IF = Text.Keyword("if");
+ protected static final Text KW_IMPORT = Text.Keyword("import");
+ protected static final Text KW_INTERFACE = Text.Keyword("interface");
+ protected static final Text KW_LET = Text.Keyword("let");
+ protected static final Text KW_MODULE = Text.Keyword("module");
+ protected static final Text KW_NEW = Text.Keyword("new");
+ protected static final Text KW_NULL = Text.Keyword("null");
+ protected static final Text KW_OUTER = Text.Keyword("outer");
+ protected static final Text KW_OVERRIDE = Text.Keyword("override");
+ protected static final Text KW_PACKAGE = Text.Keyword("package");
+ protected static final Text KW_PRIVATE = Text.Keyword("private");
+ protected static final Text KW_PROTECTED = Text.Keyword("protected");
+ protected static final Text KW_QUALIFIED = Text.Keyword("qualified");
+ protected static final Text KW_STATIC = Text.Keyword("static");
+ protected static final Text KW_SUPER = Text.Keyword("super");
+ protected static final Text KW_THIS = Text.Keyword("this");
+ protected static final Text KW_TYPE = Text.Keyword("type");
+ protected static final Text KW_VAL = Text.Keyword("val");
+ protected static final Text KW_VAR = Text.Keyword("var");
+ protected static final Text KW_WITH = Text.Keyword("with");
+ protected static final Text KW_YIELD = Text.Keyword("yield");
+
+ protected static final Text TXT_ERROR = Text.Simple("<error>");
+ protected static final Text TXT_UNKNOWN = Text.Simple("<unknown>");
+ protected static final Text TXT_NULL = Text.Simple("<null>");
+ protected static final Text TXT_MODULE_COMMENT
+ = Text.Simple("/*module*/ ");
+ protected static final Text TXT_EMPTY = Text.Simple("<empty>");
+
+ protected static final Text TXT_QUOTE = Text.Simple("\"");
+ protected static final Text TXT_PLUS = Text.Simple("+");
+ protected static final Text TXT_COLON = Text.Simple(":");
+ protected static final Text TXT_SEMICOLON = Text.Simple(";");
+ protected static final Text TXT_DOT = Text.Simple(".");
+ protected static final Text TXT_COMMA = Text.Simple(",");
+ protected static final Text TXT_EQUAL = Text.Simple("=");
+ protected static final Text TXT_SUBTYPE = Text.Simple("<:");
+ protected static final Text TXT_AT = Text.Simple("@");
+ protected static final Text TXT_HASH = Text.Simple("#");
+ protected static final Text TXT_RIGHT_ARROW = Text.Simple("=>");
+ protected static final Text TXT_LEFT_PAREN = Text.Simple("(");
+ protected static final Text TXT_RIGHT_PAREN = Text.Simple(")");
+ protected static final Text TXT_LEFT_BRACE = Text.Simple("{");
+ protected static final Text TXT_RIGHT_BRACE = Text.Simple("}");
+ protected static final Text TXT_LEFT_BRACKET = Text.Simple("[");
+ protected static final Text TXT_RIGHT_BRACKET = Text.Simple("]");
+
+ protected static final Text TXT_WITH_BLOCK_BEGIN =
+ Text.Sequence(new Text[] {
+ Text.Space, KW_WITH, Text.Space, TXT_LEFT_BRACE, Text.Newline
+ });
+ protected static final Text TXT_WITH_SP =
+ Text.Sequence(new Text[]{ Text.Space, KW_WITH, Text.Space });
+ protected static final Text TXT_BLOCK_BEGIN =
+ Text.Sequence(new Text[]{ TXT_LEFT_BRACE, Text.Newline });
+ protected static final Text TXT_BLOCK_END =
+ Text.Sequence(new Text[]{ Text.Newline, TXT_RIGHT_BRACE });
+ protected static final Text TXT_BLOCK_SEP =
+ Text.Sequence(new Text[]{ TXT_SEMICOLON, Text.Newline });
+ protected static final Text TXT_COMMA_SP =
+ Text.Sequence(new Text[]{ TXT_COMMA, Text.Space });
+ protected static final Text TXT_ELSE_NL =
+ Text.Sequence(new Text[]{ KW_ELSE, Text.Newline });
+
+ public void print(Unit unit) {
+ printUnitHeader(unit);
+ if (unit.body != null) {
+ for (int i = 0; i < unit.body.length; ++i) {
+ print(unit.body[i]);
+ print(TXT_BLOCK_SEP);
+ }
+ } else
+ print(TXT_NULL);
+ printUnitFooter(unit);
+
+ out.flush();
+ }
+
+ protected void printUnitHeader(Unit unit) {
+ print(Text.Simple("// Scala source: " + unit.source + "\n"));
+ }
+
+ protected void printUnitFooter(Unit unit) {
+ print(Text.Newline);
+ }
+
+ public TreePrinter print(Tree tree) {
+ switch (tree) {
+ case Bad():
+ print(TXT_ERROR);
+ break;
+
+ case Empty:
+ print(TXT_EMPTY);
+ break;
+
+ case ClassDef(int mods, // :
+ Name name,
+ Tree.TypeDef[] tparams,
+ Tree.ValDef[][] vparams,
+ Tree tpe,
+ Tree.Template impl):
+ printModifiers(mods);
+ print((mods & Modifiers.INTERFACE) != 0
+ ? KW_INTERFACE
+ : KW_CLASS);
+ print(Text.Space);
+ printSymbolDefinition(tree.symbol(), name);
+ printParams(tparams);
+ printParams(vparams);
+ printOpt(TXT_COLON, tpe, false);
+ printTemplate(KW_EXTENDS, impl, true);
+ break;
+
+ case PackageDef(Tree packaged, Tree.Template impl):
+ print(KW_PACKAGE);
+ print(Text.Space);
+ print(packaged);
+ print(Text.Space);
+ printTemplate(KW_WITH, impl, true);
+ break;
+
+ case ModuleDef(int mods, // :
+ Name name,
+ Tree tpe,
+ Tree.Template impl):
+ printModifiers(mods);
+ print(KW_MODULE);
+ print(Text.Space);
+ printSymbolDefinition(tree.symbol(), name);
+ printOpt(TXT_COLON, tpe, false);
+ printTemplate(KW_EXTENDS, impl, true);
+ break;
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ printModifiers(mods);
+ if ((mods & Modifiers.MUTABLE) != 0) print(KW_VAR);
+ else {
+ if ((mods & Modifiers.MODUL) != 0) print(TXT_MODULE_COMMENT);
+ print(KW_VAL);
+ }
+ print(Text.Space);
+ printSymbolDefinition(tree.symbol(), name);
+ printOpt(TXT_COLON, tpe, false);
+ printOpt(TXT_EQUAL, rhs, true);
+ break;
+
+ case PatDef(int mods, Tree pat, Tree rhs):
+ printModifiers(mods);
+ print(KW_VAL);
+ print(Text.Space);
+ print(pat);
+ printOpt(TXT_EQUAL, rhs, true);
+ break;
+
+ case DefDef(int mods,
+ Name name,
+ Tree.TypeDef[] tparams,
+ Tree.ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs):
+ printModifiers(mods);
+ if (name.isConstrName()) print(KW_CONSTR); else print(KW_DEF);
+ print(Text.Space);
+ printSymbolDefinition(tree.symbol(), name);
+ printParams(tparams);
+ printParams(vparams);
+ printOpt(TXT_COLON, tpe, false);
+ printOpt(TXT_EQUAL, rhs, true);
+ break;
+
+ case TypeDef(int mods,
+ Name name,
+ Tree.TypeDef[] tparams,
+ Tree rhs):
+ printModifiers(mods);
+ print(KW_TYPE);
+ print(Text.Space);
+ printSymbolDefinition(tree.symbol(), name);
+ printParams(tparams);
+ if ((mods & Modifiers.ABSTRACT) != 0) printOpt(TXT_SUBTYPE, rhs, true);
+ else printOpt(TXT_EQUAL, rhs, true);
+ break;
+
+ case Import(Tree expr, Name[] selectors):
+ print(KW_IMPORT);
+ print(Text.Space);
+ print(expr);
+ print(TXT_LEFT_BRACE);
+ for (int i = 0; i < selectors.length; i = i + 2) {
+ if (i > 0) print(TXT_COMMA_SP);
+ print(selectors[i].toString());
+ if (i + 1 < selectors.length && selectors[i] != selectors[i+1]) {
+ print(TXT_RIGHT_ARROW);
+ print(selectors[i+1].toString());
+ }
+ }
+ print(TXT_RIGHT_BRACE);
+ break;
+
+ case CaseDef(Tree pat, Tree guard, Tree body):
+ print(KW_CASE);
+ print(Text.Space);
+ print(pat);
+ printOpt(KW_IF, guard, true);
+ print(Text.Space);
+ print(TXT_RIGHT_ARROW);
+ print(Text.Space);
+ print(body);
+ break;
+
+ case LabelDef(Tree[] params, Tree rhs):
+ assert tree.symbol() != null;
+ printSymbolDefinition(tree.symbol(), null);
+ printArray(params, TXT_LEFT_PAREN, TXT_RIGHT_PAREN, TXT_COMMA_SP);
+ print(rhs);
+ break;
+
+ case Block(Tree[] stats):
+ printArray(stats, TXT_BLOCK_BEGIN, TXT_BLOCK_END, TXT_BLOCK_SEP);
+ break;
+
+ case Tuple(Tree[] trees):
+ printArray(trees, TXT_LEFT_BRACKET, TXT_RIGHT_BRACKET, TXT_COMMA_SP);
+ break;
+
+ case Visitor(Tree.CaseDef[] cases):
+ printArray(cases, TXT_BLOCK_BEGIN, TXT_BLOCK_END, Text.Newline);
+ break;
+
+ case Function(Tree.ValDef[] vparams, Tree body):
+ print(TXT_LEFT_BRACE);
+ printParams(vparams);
+ print(Text.Space);
+ print(TXT_RIGHT_ARROW);
+ print(Text.Space);
+ print(body);
+ print(TXT_RIGHT_BRACE);
+ break;
+
+ case Assign(Tree lhs, Tree rhs):
+ print(lhs);
+ print(Text.Space);
+ print(TXT_EQUAL);
+ print(Text.Space);
+ print(rhs);
+ break;
+
+ case If(Tree cond, Tree thenp, Tree elsep):
+ print(KW_IF);
+ print(Text.Space);
+ print(TXT_LEFT_PAREN);
+ print(cond);
+ print(TXT_RIGHT_PAREN);
+ indent(); print(Text.Newline);
+ print(thenp);
+ undent(); print(Text.Newline);
+ indent(); printOpt(TXT_ELSE_NL, elsep, false); undent();
+ printType(tree);
+ break;
+
+ case New(Tree.Template templ):
+ printTemplate(KW_NEW, templ, false);
+ printType(tree);
+ break;
+
+ case Typed(Tree expr, Tree tpe):
+ print(TXT_LEFT_PAREN);
+ print(expr);
+ print(TXT_RIGHT_PAREN);
+ print(Text.Space);
+ print(TXT_COLON);
+ print(Text.Space);
+ print(tpe);
+ printType(tree);
+ break;
+
+ case TypeApply(Tree fun, Tree[] targs):
+ print(fun);
+ print(TXT_AT);
+ printArray(targs, TXT_LEFT_BRACKET, TXT_RIGHT_BRACKET, TXT_COMMA_SP);
+ printType(tree);
+ break;
+
+ case Apply(Tree fun, Tree[] vargs):
+ print(fun);
+ printArray(vargs, TXT_LEFT_PAREN, TXT_RIGHT_PAREN, TXT_COMMA_SP);
+ printType(tree);
+ break;
+
+ case Super(Tree tpe):
+ if (tpe != Tree.Empty)
+ print(TXT_LEFT_PAREN);
+
+ print(KW_SUPER);
+
+ if (tpe != Tree.Empty) {
+ print(Text.Space);
+ print(TXT_COLON);
+ print(Text.Space);
+ print(tpe);
+ print(TXT_RIGHT_PAREN);
+ }
+ printType(tree);
+ break;
+
+ case This(Tree qualifier):
+ if (qualifier != Tree.Empty) {
+ print(qualifier);
+ print(TXT_DOT);
+ }
+ print(KW_THIS);
+ printType(tree);
+ break;
+
+ case Select(Tree qualifier, Name name):
+ print(qualifier);
+ print(TXT_DOT);
+ printSymbolUse(tree.symbol(), name);
+ printType(tree);
+ break;
+
+ case Ident(Name name):
+ printSymbolUse(tree.symbol(), name);
+ printType(tree);
+ break;
+
+ case Literal(Object obj):
+ String str;
+ if (obj instanceof String)
+ str = "\"" + obj + "\"";
+ else
+ str = String.valueOf(obj);
+ print(Text.Literal(str));
+ printType(tree);
+ break;
+
+ case SingletonType(Tree ref):
+ print(ref);
+ print(TXT_DOT); print(KW_TYPE);
+ break;
+
+ case SelectFromType(Tree qualifier, Name selector):
+ print(qualifier);
+ print(Text.Space); print(TXT_HASH); print(Text.Space);
+ printSymbolUse(tree.symbol(), selector);
+ break;
+
+ case FunType(Tree[] argtpes, Tree restpe):
+ printArray(argtpes, TXT_LEFT_PAREN, TXT_RIGHT_PAREN, TXT_COMMA_SP);
+ print(TXT_RIGHT_ARROW);
+ print(restpe);
+ break;
+
+ case CompoundType(Tree[] baseTypes, Tree[] refinements):
+ printArray(baseTypes, Text.None, Text.None, TXT_WITH_SP);
+ printArray(refinements, TXT_WITH_BLOCK_BEGIN, TXT_BLOCK_END, Text.Newline);
+ break;
+
+ case TupleType(Tree[] types):
+ printArray(types, TXT_LEFT_BRACKET, TXT_RIGHT_BRACKET, TXT_COMMA_SP);
+ break;
+
+ case AppliedType(Tree tpe, Tree[] args):
+ print(tpe);
+ indent();
+ print(TXT_LEFT_BRACKET);
+ for (int i = 0; i < args.length; ++i) {
+ if (i > 0) print(TXT_COMMA_SP);
+ print(args[i]);
+ }
+ undent();
+ print(TXT_RIGHT_BRACKET);
+ break;
+
+ case CovariantType(Tree tpe):
+ print(TXT_PLUS);
+ print(tpe);
+ break;
+
+ case Template(Tree[] parents, Tree[] body):
+ Debug.abort("unexpected case", tree);
+ break;
+
+ default:
+ print(TXT_UNKNOWN);
+ break;
+ }
+ return this;
+ }
+
+ // Printing helpers
+
+ protected void printArray(Tree[] trees, Text open, Text close, Text sep) {
+ indent();
+ print(open);
+ for (int i = 0; i < trees.length; ++i) {
+ if (i > 0) print(sep);
+ print(trees[i]);
+ }
+ undent();
+ print(close);
+ }
+
+ protected void printOpt(Text prefix, Tree tree, boolean spaceBefore) {
+ if (tree != Tree.Empty) {
+ if (spaceBefore)
+ print(Text.Space);
+ print(prefix);
+ print(Text.Space);
+ print(tree);
+ }
+ }
+
+ // Printing of symbols
+
+ protected String symbolString(Symbol symbol, Name name) {
+ if (symbol != null)
+ return symbol.name.toString();
+ else
+ return name.toString();
+ }
+
+ protected void printSymbolDefinition(Symbol symbol, Name name) {
+ print(Text.Identifier(symbol,
+ symbolString(symbol, name),
+ SymbolUsage.Definition));
+ }
+
+ protected void printSymbolUse(Symbol symbol, Name name) {
+ print(Text.Identifier(symbol,
+ symbolString(symbol, name),
+ SymbolUsage.Use));
+ }
+
+ // Printing of trees
+
+ protected void printType(Tree tree) {
+ if (Global.instance.printtypes) {
+ print(TXT_LEFT_BRACE);
+ if (tree.type != null)
+ print(Text.Simple(tree.type.toString()));
+ else
+ print(TXT_NULL);
+ print(TXT_RIGHT_BRACE);
+ }
+ }
+
+ protected void printModifiers(int flags) {
+ if ((flags & Modifiers.ABSTRACT) != 0) {
+ print(KW_ABSTRACT);
+ print(Text.Space);
+ }
+ if ((flags & Modifiers.FINAL) != 0) {
+ print(KW_FINAL);
+ print(Text.Space);
+ }
+ if ((flags & Modifiers.PRIVATE) != 0) {
+ print(KW_PRIVATE);
+ print(Text.Space);
+ }
+ if ((flags & Modifiers.PROTECTED) != 0) {
+ print(KW_PROTECTED);
+ print(Text.Space);
+ }
+ if ((flags & Modifiers.QUALIFIED) != 0) {
+ print(KW_QUALIFIED);
+ print(Text.Space);
+ }
+ if ((flags & Modifiers.OVERRIDE) != 0) {
+ print(KW_OVERRIDE);
+ print(Text.Space);
+ }
+ if ((flags & Modifiers.CASE) != 0) {
+ print(KW_CASE);
+ print(Text.Space);
+ }
+ if ((flags & Modifiers.DEF) != 0) {
+ print(KW_DEF);
+ print(Text.Space);
+ }
+ if ((flags & Modifiers.STATIC) != 0) {
+ print(KW_STATIC);
+ print(Text.Space);
+ }
+ }
+
+ protected void printTemplate(Text prefix,
+ Tree.Template templ,
+ boolean spaceBefore) {
+ if (! (templ.parents.length == 0
+ || (templ.parents.length == 1
+ && templ.parents[0] == Tree.Empty))) {
+ if (spaceBefore)
+ print(Text.Space);
+ print(prefix);
+ print(Text.Space);
+ printArray(templ.parents, Text.None, Text.None, TXT_WITH_SP);
+ }
+
+ if (templ.body.length > 0)
+ printArray(templ.body, TXT_WITH_BLOCK_BEGIN, TXT_BLOCK_END, TXT_BLOCK_SEP);
+ }
+
+ protected void printParams(Tree.TypeDef[] tparams) {
+ if (tparams.length > 0) {
+ print(TXT_LEFT_BRACKET);
+ for (int i = 0; i < tparams.length; i++) {
+ if (i > 0) print(TXT_COMMA_SP);
+ printParam(tparams[i]);
+ }
+ print(TXT_RIGHT_BRACKET);
+ }
+ }
+
+ protected void printParams(Tree.ValDef[][] vparamss) {
+ for (int i = 0; i < vparamss.length; ++i)
+ printParams(vparamss[i]);
+ }
+
+ protected void printParams(Tree.ValDef[] vparams) {
+ print(TXT_LEFT_PAREN);
+ for (int i = 0; i < vparams.length; ++i) {
+ if (i > 0) print(TXT_COMMA_SP);
+ printParam(vparams[i]);
+ }
+ print(TXT_RIGHT_PAREN);
+ }
+
+ protected void printParam(Tree tree) {
+ switch (tree) {
+ case TypeDef(int mods, Name name, _, Tree bound):
+ printModifiers(mods);
+ printSymbolDefinition(tree.symbol(), name);
+ printOpt(KW_EXTENDS, bound, true);
+ break;
+
+ case ValDef(int mods, Name name, Tree tpe, Tree.Empty):
+ printModifiers(mods);
+ printSymbolDefinition(tree.symbol(), name);
+ printOpt(TXT_COLON, tpe, false);
+ break;
+
+ default:
+ Debug.abort("bad parameter: " + tree);
+ }
+ }
+}
diff --git a/sources/scalac/ast/printer/TreePrinter.java b/sources/scalac/ast/printer/TreePrinter.java
new file mode 100644
index 0000000000..059b335c72
--- /dev/null
+++ b/sources/scalac/ast/printer/TreePrinter.java
@@ -0,0 +1,34 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.ast.printer;
+
+import java.io.OutputStream;
+
+import scalac.Unit;
+import scalac.ast.Tree;
+
+/**
+ * Interface for all abstract tree printers.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+public interface TreePrinter {
+ public void begin();
+ public void end();
+ public void flush();
+
+ public void beginSection(int level, String title);
+
+ public void print(Unit unit);
+
+ public TreePrinter print(Tree tree);
+ public TreePrinter print(String str);
+ public TreePrinter println();
+}
diff --git a/sources/scalac/checkers/CheckOwners.java b/sources/scalac/checkers/CheckOwners.java
new file mode 100644
index 0000000000..f23bcb4d7e
--- /dev/null
+++ b/sources/scalac/checkers/CheckOwners.java
@@ -0,0 +1,168 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.checkers;
+
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import scalac.Global;
+import scalac.util.Debug;
+import Tree.*;
+
+/**
+ * Check that the owner of symbols is set correctly.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+
+public class CheckOwners extends Checker {
+ protected Symbol currentOwner;
+
+ public CheckOwners(Global global) {
+ super(global);
+ currentOwner = global.definitions.ROOT_CLASS;
+ }
+
+ protected void traverse(Tree tree, Symbol owner) {
+ Symbol prevOwner = currentOwner;
+ currentOwner = owner;
+ traverse(tree);
+ currentOwner = prevOwner;
+ }
+
+ protected void traverse(Tree[] array, Symbol owner) {
+ Symbol prevOwner = currentOwner;
+ currentOwner = owner;
+ traverse(array);
+ currentOwner = prevOwner;
+ }
+
+ protected void traverse(Tree[][] array, Symbol owner) {
+ Symbol prevOwner = currentOwner;
+ currentOwner = owner;
+ traverse(array);
+ currentOwner = prevOwner;
+ }
+
+ protected void traverse(Template templ, Symbol owner) {
+ Symbol prevOwner = currentOwner;
+ if (owner.kind == Kinds.CLASS)
+ currentOwner = owner.constructor();
+ traverse(templ.parents);
+ currentOwner = owner;
+
+ Symbol templSymbol = templ.symbol();
+ Tree[] body = templ.body;
+ for (int i = 0; i < body.length; ++i) {
+ switch (body[i]) {
+ case PackageDef(_,_):
+ case ClassDef(_,_,_,_,_,_):
+ case ModuleDef(_,_,_,_):
+ case DefDef(_,_,_,_,_,_):
+ case ValDef(_,_,_,_):
+ case TypeDef(_,_,_,_):
+ traverse(body[i], owner);
+ break;
+ default:
+ traverse(body[i], templSymbol);
+ }
+ }
+
+ currentOwner = prevOwner;
+ }
+
+ protected void checkOwner(Tree tree, Symbol sym) {
+ Symbol owner = sym.owner();
+ verify(tree,
+ owner == currentOwner,
+ "owner",
+ "incorrect owner for " + Debug.toString(sym) + ":\n"
+ + " found: " + Debug.toString(owner) + "\n"
+ + " required: " + Debug.toString(currentOwner));
+ }
+
+ public void traverse(Tree tree) {
+ switch(tree) {
+ case PackageDef(Tree packaged, Template impl):
+ check(tree);
+ traverse(packaged);
+ traverse(impl, packaged.symbol());
+ break;
+
+ case ClassDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Template impl): {
+ check(tree);
+ traverse(tparams, tree.symbol());
+ traverse(vparams, tree.symbol());
+ traverse(tpe);
+ traverse(impl, tree.symbol());
+ } break;
+
+ case ModuleDef(int mods, Name name, Tree tpe, Template impl): {
+ check(tree);
+ traverse(tpe);
+ traverse(impl, tree.symbol());
+ } break;
+
+ case DefDef(int mods,
+ Name name,
+ TypeDef[] tparams,
+ ValDef[][] vparams,
+ Tree tpe,
+ Tree rhs): {
+ check(tree);
+ traverse(tparams, tree.symbol());
+ traverse(vparams, tree.symbol());
+ traverse(tpe, tree.symbol());
+ traverse(rhs, tree.symbol());
+ } break;
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs): {
+ check(tree);
+ traverse(tpe);
+ traverse(rhs, tree.symbol());
+ } break;
+
+ case TypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs): {
+ check(tree);
+ traverse(tparams, tree.symbol());
+ traverse(rhs, tree.symbol());
+ } break;
+
+ default:
+ super.traverse(tree);
+ }
+ }
+
+ public void check(Tree tree) {
+ switch (tree) {
+ case PackageDef(_,_):
+ case ClassDef(_,_,_,_,_,_):
+ case ModuleDef(_,_,_,_):
+ case DefDef(_,_,_,_,_,_):
+ case ValDef(_,_,_,_):
+ case TypeDef(_,_,_,_): {
+ Symbol sym = tree.symbol();
+ if (sym != null && sym != Symbol.NONE) {
+ checkOwner(tree, sym);
+ if (sym.kind == Kinds.CLASS)
+ checkOwner(tree, sym.constructor());
+ }
+ } break;
+
+ default:
+ ; // nothing to do
+ }
+ }
+}
diff --git a/sources/scalac/checkers/CheckSymbols.java b/sources/scalac/checkers/CheckSymbols.java
new file mode 100644
index 0000000000..ab1a76ae2d
--- /dev/null
+++ b/sources/scalac/checkers/CheckSymbols.java
@@ -0,0 +1,36 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.checkers;
+
+import scalac.ast.Tree;
+import scalac.symtab.Symbol;
+import scalac.Global;
+
+/**
+ * Verify that all tree nodes for which hasSymbol() is true have a
+ * non-null symbol.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+
+public class CheckSymbols extends Checker {
+ public CheckSymbols(Global global) { super(global); }
+
+ public void check(Tree tree) {
+ verify(tree,
+ implies(tree.hasSymbol(), tree.symbol() != null),
+ "symbol not null",
+ "hasSymbol => symbol not null");
+ verify(tree,
+ implies(tree.hasSymbol(), tree.symbol() != Symbol.NONE),
+ "symbol not NONE",
+ "hasSymbol => symbol not NONE");
+ }
+}
diff --git a/sources/scalac/checkers/CheckTypes.java b/sources/scalac/checkers/CheckTypes.java
new file mode 100644
index 0000000000..cfe2bd656d
--- /dev/null
+++ b/sources/scalac/checkers/CheckTypes.java
@@ -0,0 +1,30 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.checkers;
+
+import scalac.ast.Tree;
+import scalac.util.Name;
+import scalac.symtab.Type;
+import scalac.Global;
+import scalac.util.Debug;
+
+/**
+ * Check that all tree nodes have a type.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+
+public class CheckTypes extends Checker {
+ public CheckTypes(Global global) { super(global); }
+
+ public void check(Tree tree) {
+ verify(tree, tree.type != null, "non-null type", "type of tree is not null");
+ }
+}
diff --git a/sources/scalac/checkers/Checker.java b/sources/scalac/checkers/Checker.java
new file mode 100644
index 0000000000..ae52a5b1f6
--- /dev/null
+++ b/sources/scalac/checkers/Checker.java
@@ -0,0 +1,41 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.checkers;
+
+import scalac.ast.*;
+import scalac.Global;
+
+public abstract class Checker extends Traverser {
+ protected final Global global;
+
+ public Checker(Global global) {
+ this.global = global;
+ }
+
+ public boolean implies(boolean b1, boolean b2) {
+ return (!b1) | b2;
+ }
+
+ public void verify(Tree tree, boolean b, String name, String message) {
+ if (! b) {
+ System.err.println("ERROR: Condition '" + name + "' violated (after "
+ + global.currentPhase + ")!");
+ System.err.println(message);
+ global.debugPrinter.print(tree);
+ System.err.println();
+ }
+ }
+
+ abstract public void check(Tree tree);
+
+ public void traverse(Tree tree) {
+ check(tree);
+ super.traverse(tree);
+ }
+}
diff --git a/sources/scalac/symtab/Definitions.java b/sources/scalac/symtab/Definitions.java
new file mode 100644
index 0000000000..6d67edac42
--- /dev/null
+++ b/sources/scalac/symtab/Definitions.java
@@ -0,0 +1,421 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab;
+
+import scalac.*;
+import scalac.util.*;
+import scalac.symtab.classfile.*;
+import Type.*;
+
+public class Definitions {
+
+ /** the root module
+ */
+ public final Symbol ROOT;
+ public final Symbol ROOT_CLASS;
+ public final Type ROOT_TYPE;
+
+ /** the scala module
+ */
+ public final Symbol SCALA;
+ public final Symbol SCALA_CLASS;
+ public final Type SCALA_TYPE;
+
+ /** the java module
+ */
+ public final Symbol JAVA;
+ public final Symbol JAVA_CLASS;
+ public final Type JAVA_TYPE;
+
+ /** the java.lang module
+ */
+ public final Symbol JAVALANG;
+ public final Symbol JAVALANG_CLASS;
+ public final Type JAVALANG_TYPE;
+
+ /** the scala.runtime module
+ */
+ public final Symbol SCALARUNTIME;
+ public final Symbol SCALARUNTIME_CLASS;
+ public final Type SCALARUNTIME_TYPE;
+
+ /** the null value
+ */
+ public final Symbol NULL;
+
+ /** the scala.Any class
+ */
+ public final Symbol ANY_CLASS;
+ public final Type ANY_TYPE;
+
+ /** some methods of the scala.Any class
+ */
+ public final Symbol MATCH;
+ public final Symbol IS;
+ public final Symbol AS;
+ public final Symbol EQEQ;
+ public final Symbol BANGEQ;
+ public final Symbol TOSTRING;
+ public final Symbol HASHCODE;
+
+ /** a method of class java.lang.Throwable
+ */
+ public final Symbol THROW;
+
+ /** the scala.AnyVal class
+ */
+ public final Symbol ANYVAL_CLASS;
+ public final Type ANYVAL_TYPE;
+
+ /** the scala.AnyRef class
+ */
+ public final Symbol ANYREF_CLASS;
+ public final Type ANYREF_TYPE;
+
+ /** the java.lang.Object class
+ */
+ public final Symbol JAVA_OBJECT_CLASS;
+ public final Type JAVA_OBJECT_TYPE;
+
+ /** the scala.Object class
+ */
+ public final Symbol OBJECT_CLASS;
+ public final Type OBJECT_TYPE;
+
+ /** the primitive types
+ */
+ public final Symbol BYTE_CLASS;
+ public final Type BYTE_TYPE;
+ public final Symbol SHORT_CLASS;
+ public final Type SHORT_TYPE;
+ public final Symbol CHAR_CLASS;
+ public final Type CHAR_TYPE;
+ public final Symbol INT_CLASS;
+ public final Type INT_TYPE;
+ public final Symbol LONG_CLASS;
+ public final Type LONG_TYPE;
+ public final Symbol FLOAT_CLASS;
+ public final Type FLOAT_TYPE;
+ public final Symbol DOUBLE_CLASS;
+ public final Type DOUBLE_TYPE;
+ public final Symbol BOOLEAN_CLASS;
+ public final Type BOOLEAN_TYPE;
+ public final Symbol UNIT_CLASS;
+ public final Type UNIT_TYPE;
+
+ /** the array class
+ */
+ public final Symbol ARRAY_CLASS;
+
+ /** types from java.lang
+ */
+ public final Symbol JAVA_STRING_CLASS;
+ public final Type JAVA_STRING_TYPE;
+ public final Symbol JAVA_THROWABLE_CLASS;
+ public final Type JAVA_THROWABLE_TYPE;
+
+ /** types from scala
+ */
+ public final Symbol STRING_CLASS;
+ public final Type STRING_TYPE;
+
+ /** string concatenation pseudo-methods of classes scala.Any and
+ * java.lang.String
+ */
+ //public final Symbol ANY_PLUS_STRING;
+ public final Symbol STRING_PLUS_ANY;
+
+ /** members of class Boolean
+ */
+ private Symbol TRUE;
+ private Symbol FALSE;
+ private Symbol BARBAR;
+ private Symbol AMPAMP;
+
+ public Symbol TRUE () { loadBooleanMembers(); return TRUE ; }
+ public Symbol FALSE () { loadBooleanMembers(); return FALSE ; }
+ public Symbol BARBAR() { loadBooleanMembers(); return BARBAR; }
+ public Symbol AMPAMP() { loadBooleanMembers(); return AMPAMP; }
+
+ public Definitions(Global global) {
+ // a hack to make definitions accessible earlier to other
+ // components
+ global.definitions = this;
+ PackageParser pparser = new PackageParser(global);
+
+ // this is the root value; all top-level functions,
+ // modules etc. are a member of this value
+ ROOT = TermSymbol.newJavaPackageModule(
+ Names.EMPTY, Symbol.NONE, pparser);
+ ROOT_CLASS = ROOT.moduleClass();
+ // this is a prefix for all types inside of the anonymous package
+ ROOT_TYPE = ROOT_CLASS.thisType();
+
+ // the scala module
+ SCALA = getModule(Names.scala);
+ // the scala class
+ SCALA_CLASS = SCALA.moduleClass();
+ // the scala package as a prefix
+ SCALA_TYPE = Type.singleType(ROOT_TYPE, SCALA);
+
+ // the java module
+ JAVA = getModule(Names.java);
+ // the java class
+ JAVA_CLASS = JAVA.moduleClass();
+ // the java package as a prefix
+ JAVA_TYPE = Type.singleType(ROOT_TYPE, JAVA);
+
+ // the java.lang module
+ JAVALANG = getModule(Names.java_lang);
+ // the java.lang class
+ JAVALANG_CLASS = JAVALANG.moduleClass();
+ // the java.lang package as a prefix
+ JAVALANG_TYPE = Type.singleType(JAVA_TYPE, JAVALANG);
+
+ // the scala.runtime module
+ SCALARUNTIME = getModule(Names.scala_runtime);
+ // the scala.runtime class
+ SCALARUNTIME_CLASS = SCALARUNTIME.moduleClass();
+ // the scala.runtime package as a prefix
+ SCALARUNTIME_TYPE = Type.singleType(SCALA_TYPE, SCALARUNTIME);
+
+ // the scala.ANY classs
+ ANY_CLASS = new ClassSymbol(
+ Position.NOPOS, Names.Any.toTypeName(), SCALA_CLASS, Modifiers.JAVA);
+ SCALA_CLASS.members().enter(ANY_CLASS);
+ ANY_TYPE = monoType(ANY_CLASS);
+ ANY_CLASS.setInfo(Type.compoundType(Type.EMPTY_ARRAY, new Scope(), ANY_CLASS));
+ ANY_CLASS.constructor().setInfo(
+ Type.PolyType(Symbol.EMPTY_ARRAY, ANY_TYPE));
+
+ // the java.lang.OBJECT class
+ JAVA_OBJECT_CLASS = getClass(Names.java_lang_Object);
+ JAVA_OBJECT_TYPE = monoType(JAVA_OBJECT_CLASS);
+ JAVA_OBJECT_CLASS.setInfo(pparser.classCompletion);
+
+ // the scala.ANYREF class
+ ANYREF_CLASS = new TypeSymbol(
+ Kinds.ALIAS, Position.NOPOS, Names.AnyRef.toTypeName(),
+ SCALA_CLASS, Modifiers.JAVA)
+ .setInfo(JAVA_OBJECT_TYPE);
+ ANYREF_CLASS.constructor().setInfo(
+ Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType));
+
+ ANYREF_TYPE = monoType(ANYREF_CLASS);
+ SCALA.members().enter(ANYREF_CLASS);
+
+ // the scala.OBJECT class
+ OBJECT_CLASS = getClass(Names.scala_Object);
+ OBJECT_TYPE = monoType(OBJECT_CLASS);
+
+ // the scala.ANYVAL class
+ ANYVAL_CLASS = getClass(Names.scala_AnyVal);
+ ANYVAL_TYPE = monoType(ANYVAL_CLASS);
+
+ // the primitive types
+ DOUBLE_CLASS = getClass(Names.scala_Double);
+ DOUBLE_TYPE = monoType(DOUBLE_CLASS);
+ FLOAT_CLASS = getClass(Names.scala_Float);
+ FLOAT_TYPE = monoType(FLOAT_CLASS);
+ LONG_CLASS = getClass(Names.scala_Long);
+ LONG_TYPE = monoType(LONG_CLASS);
+ INT_CLASS = getClass(Names.scala_Int);
+ INT_TYPE = monoType(INT_CLASS);
+ CHAR_CLASS = getClass(Names.scala_Char);
+ CHAR_TYPE = monoType(CHAR_CLASS);
+ SHORT_CLASS = getClass(Names.scala_Short);
+ SHORT_TYPE = monoType(SHORT_CLASS);
+ BYTE_CLASS = getClass(Names.scala_Byte);
+ BYTE_TYPE = monoType(BYTE_CLASS);
+ BOOLEAN_CLASS = getClass(Names.scala_Boolean);
+ BOOLEAN_TYPE = monoType(BOOLEAN_CLASS);
+ UNIT_CLASS = getClass(Names.scala_Unit);
+ UNIT_TYPE = monoType(UNIT_CLASS);
+
+ // the array class
+ ARRAY_CLASS = getClass(Names.scala_Array);
+
+ // add members to java.lang.Throwable
+ JAVA_THROWABLE_CLASS = getClass(Names.java_lang_Throwable);
+ JAVA_THROWABLE_TYPE = monoType(JAVA_THROWABLE_CLASS);
+ THROW = new TermSymbol(
+ Position.NOPOS, Names.throw_, JAVA_THROWABLE_CLASS, Modifiers.FINAL);
+ Symbol tvar = newTypeParameter(THROW, ANY_TYPE);
+ THROW.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type()));
+ JAVA_THROWABLE_CLASS.members().enter(THROW);
+
+ // add the java.lang.String class to the scala package
+ JAVA_STRING_CLASS = getClass(Names.java_lang_String);
+ JAVA_STRING_TYPE = monoType(JAVA_STRING_CLASS);
+ STRING_CLASS = new TypeSymbol(
+ Kinds.ALIAS, Position.NOPOS, Names.String.toTypeName(), SCALA_CLASS, 0)
+ .setInfo(JAVA_STRING_TYPE);
+ STRING_CLASS.constructor().setInfo(
+ Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType));
+ STRING_TYPE = monoType(STRING_CLASS);
+ SCALA.members().enter(STRING_CLASS);
+
+ /*
+ ANY_PLUS_STRING = new TermSymbol(
+ Position.NOPOS, Names.PLUS, ANY_CLASS, Modifiers.FINAL);
+ ANY_PLUS_STRING.setInfo(
+ Type.MethodType(
+ new Symbol[]{newParameter(ANY_PLUS_STRING, STRING_TYPE)},
+ STRING_TYPE));
+ ANY_CLASS.members().enter(ANY_PLUS_STRING);
+ */
+
+ STRING_PLUS_ANY = new TermSymbol(
+ Position.NOPOS, Names.PLUS, STRING_CLASS, Modifiers.FINAL);
+ STRING_PLUS_ANY.setInfo(
+ Type.MethodType(
+ new Symbol[]{newParameter(STRING_PLUS_ANY, ANY_TYPE)},
+ STRING_TYPE));
+ STRING_CLASS.members().enter(STRING_PLUS_ANY);
+
+ // add members to class scala.Any
+ MATCH = new TermSymbol(
+ Position.NOPOS, Names.match, ANY_CLASS, Modifiers.FINAL);
+ MATCH.setInfo(
+ Type.MethodType(
+ new Symbol[]{newParameter(MATCH, OBJECT_TYPE)},
+ OBJECT_TYPE));
+ ANY_CLASS.members().enter(MATCH);
+
+ AS = new TermSymbol(
+ Position.NOPOS, Names.as, ANY_CLASS, Modifiers.FINAL);
+ tvar = newTypeParameter(AS, ANY_TYPE);
+ AS.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type()));
+ ANY_CLASS.members().enter(AS);
+
+ IS = new TermSymbol(
+ Position.NOPOS, Names.is, ANY_CLASS, Modifiers.FINAL);
+ IS.setInfo(Type.PolyType(new Symbol[]{newTypeParameter(IS, ANY_TYPE)},
+ BOOLEAN_TYPE));
+ ANY_CLASS.members().enter(IS);
+
+ EQEQ = new TermSymbol(
+ Position.NOPOS, Names.EQEQ, ANY_CLASS, 0);
+ EQEQ.setInfo(Type.MethodType(new Symbol[]{newParameter(EQEQ, ANY_TYPE)},
+ BOOLEAN_TYPE));
+ ANY_CLASS.members().enter(EQEQ);
+
+ BANGEQ = new TermSymbol(
+ Position.NOPOS, Names.BANGEQ, ANY_CLASS, 0);
+ BANGEQ.setInfo(Type.MethodType(new Symbol[]{newParameter(BANGEQ, ANY_TYPE)},
+ BOOLEAN_TYPE));
+ ANY_CLASS.members().enter(BANGEQ);
+
+ TOSTRING = new TermSymbol(
+ Position.NOPOS, Names.toString, ANY_CLASS, 0);
+ TOSTRING.setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, STRING_TYPE));
+ ANY_CLASS.members().enter(TOSTRING);
+
+ HASHCODE = new TermSymbol(
+ Position.NOPOS, Names.hashCode, ANY_CLASS, 0);
+ HASHCODE.setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, INT_TYPE));
+ ANY_CLASS.members().enter(HASHCODE);
+
+ // add a null value to the root scope
+ NULL = new TermSymbol(
+ Position.NOPOS, Names.null_, ROOT_CLASS, 0);
+ tvar = newTypeParameter(NULL, ANYREF_TYPE);
+ NULL.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type()));
+ ROOT.members().enter(NULL);
+ }
+
+ private Symbol newParameter(Symbol owner, Type tp) {
+ return new TermSymbol(Position.NOPOS, Name.fromString("v"), owner, Modifiers.PARAM)
+ .setInfo(tp);
+ }
+
+ private Symbol newTypeParameter(Symbol owner, Type bound) {
+ return new TypeSymbol(
+ Kinds.TYPE, Position.NOPOS, Name.fromString("T").toTypeName(), owner, Modifiers.PARAM)
+ .setInfo(bound);
+ }
+
+ public Symbol getModule(Name fullname) {
+ Scope scope = ROOT_CLASS.members();
+ int i = 0;
+ int j = fullname.pos((byte)'.', i);
+ while (j < fullname.length()) {
+ scope = scope.lookup(fullname.subName(i, j)).members();
+ i = j + 1;
+ j = fullname.pos((byte)'.', i);
+ }
+ Symbol sym = scope.lookup(fullname.subName(i, fullname.length()));
+ if (!sym.isModule()) {
+ switch (sym.type()) {
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ for (int k = 0; k < alts.length; k++)
+ if ((sym = alts[k]).isModule()) break;
+ }
+ }
+ assert sym.isModule() : "no module '" + fullname + "'";
+ return sym;
+ }
+
+ public Symbol getClass(Name fullname) {
+ Scope scope = ROOT_CLASS.members();
+ int i = 0;
+ int j = fullname.pos((byte)'.', i);
+ while (j < fullname.length()) {
+ scope = scope.lookup(fullname.subName(i, j)).members();
+ i = j + 1;
+ j = fullname.pos((byte)'.', i);
+ }
+ Symbol sym = scope.lookup(fullname.subName(i, fullname.length()).toTypeName());
+ assert sym.kind != Kinds.NONE : "no class '" + fullname + "'";
+ return sym;
+ }
+
+ public Type getType(Name fullname) {
+ return getClass(fullname).type();
+ }
+
+ public Type monoType(Symbol c) {
+ return Type.TypeRef(c.owner().thisType(), c, Type.EMPTY_ARRAY);
+ }
+
+ public Type getJavaType(Name fullname) {
+ return monoType(getClass(fullname));
+ }
+
+ private void loadBooleanMembers() {
+ if (TRUE != null) return;
+ Symbol booleanStatics = getModule(Names.scala_Boolean);
+ TRUE = booleanStatics.members().lookup(Names.True);
+ FALSE = booleanStatics.members().lookup(Names.False);
+ BARBAR = BOOLEAN_TYPE.lookup(Names.BARBAR);
+ AMPAMP = BOOLEAN_TYPE.lookup(Names.AMPAMP);
+ }
+
+ public Type arrayType(Type elemtpe) {
+ return Type.appliedType(monoType(ARRAY_CLASS), new Type[]{elemtpe});
+ }
+
+ public Type functionType(Type[] argtps, Type restp) {
+ Type[] argtps1 = new Type[argtps.length + 1];
+ System.arraycopy(argtps, 0, argtps1, 0, argtps.length);
+ argtps1[argtps.length] = Type.covarType(restp);
+ return Type.appliedType(
+ getType(Name.fromString("scala.Function" + argtps.length)),
+ argtps1);
+ }
+
+ public Type tupleType(Type[] args) {
+ assert args.length > 0;
+ Type[] args1 = new Type[args.length];
+ for (int i = 0; i < args.length; i++)
+ args1[i] = Type.covarType(args[i]);
+ return Type.appliedType(
+ getType(Name.fromString("scala.Tuple" + args.length)), args1);
+ }
+}
diff --git a/sources/scalac/symtab/Kinds.java b/sources/scalac/symtab/Kinds.java
new file mode 100644
index 0000000000..8902ff4699
--- /dev/null
+++ b/sources/scalac/symtab/Kinds.java
@@ -0,0 +1,28 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab;
+
+
+public interface Kinds {
+
+ /** kind of error symbol
+ */
+ int ERROR = 0;
+
+ /** kind of non-existent symbol
+ */
+ int NONE = 1;
+
+ /** definition kinds
+ */
+ int ALIAS = 2;
+ int CLASS = 3;
+ int TYPE = 4;
+ int VAL = 5;
+}
diff --git a/sources/scalac/symtab/Modifiers.java b/sources/scalac/symtab/Modifiers.java
new file mode 100644
index 0000000000..3fc119419e
--- /dev/null
+++ b/sources/scalac/symtab/Modifiers.java
@@ -0,0 +1,135 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab;
+
+public interface Modifiers {
+
+ // modifiers
+ int ABSTRACT = 0x00000001;
+ int FINAL = 0x00000002;
+ int PRIVATE = 0x00000004;
+ int PROTECTED = 0x00000008;
+
+ int QUALIFIED = 0x00000010;
+ int OVERRIDE = 0x00000020;
+ int CASE = 0x00000040;
+ int ABSTRACTCLASS = 0x00000080; // abstract class
+
+ int DEF = 0x00000100; // a def parameter
+ int SYNTHETIC = 0x00000200;
+ int DEPRECATED = 0x00000400;
+ int JAVA = 0x00000800; // symbol was defined by a Java class
+
+ int MODUL = 0x00001000; // symbol is module or class implementing a module
+ int MUTABLE = 0x00002000; // symbol is a mutable variable.
+ int PARAM = 0x00004000; // symbol is a (type) parameter to a method
+
+ int INITIALIZED = 0x00010000; // symbol's definition is complete
+ int LOCKED = 0x00020000; // temporary flag to catch cyclic dependencies
+ int ACCESSED = 0x00040000; // symbol was accessed at least once
+ int SELECTOR = 0x00080000; // symbol was used as selector in Select
+
+ int PACKAGE = 0x00100000; // symbol is a java packages.
+ int LABEL = 0x00200000; // symbol is a label symbol
+ int STATIC = 0x00400000; // "static" inner classes (i.e. after class norm.)
+ int STABLE = 0x00800000; // functions that are assumed to be stable
+ // (typically, access methods for valdefs)
+
+ int CAPTURED = 0x01000000; // variables is accessed from nested function.
+
+ int ACCESSOR = 0x04000000; // function is an access function for a
+ // value or variable
+ int BRIDGE = 0x0800000; // function is a bridge method.
+
+ int INTERFACE = 0x10000000; // symbol is a Java interface
+ int TRAIT = 0x20000000; // symbol is a Trait
+
+ int SNDTIME = 0x40000000; //debug
+
+ // masks
+ int SOURCEFLAGS = 0x00000077 | PARAM | TRAIT; // these modifiers can be set in source programs.
+ int ACCESSFLAGS = PRIVATE | PROTECTED;
+
+ public static class Helper {
+
+ public static boolean isAbstract(int flags) {
+ return (flags & (ABSTRACT | ABSTRACTCLASS)) != 0;
+ }
+
+ public static boolean isFinal(int flags) {
+ return (flags & FINAL) != 0;
+ }
+
+ public static boolean isPrivate(int flags) {
+ return (flags & PRIVATE) != 0;
+ }
+
+ public static boolean isProtected(int flags) {
+ return (flags & PROTECTED) != 0;
+ }
+
+ public static boolean isQualified(int flags) {
+ return (flags & QUALIFIED) != 0;
+ }
+
+ public static boolean isOverride(int flags) {
+ return (flags & OVERRIDE) != 0;
+ }
+
+ public static boolean isCase(int flags) {
+ return (flags & CASE) != 0;
+ }
+
+ public static boolean isInterface(int flags) {
+ return (flags & INTERFACE) != 0;
+ }
+
+ public static boolean isDef(int flags) {
+ return (flags & DEF) != 0;
+ }
+
+ public static boolean isModClass(int flags) {
+ return (flags & MODUL) != 0;
+ }
+
+ public static boolean isStatic(int flags) {
+ return (flags & STATIC) != 0;
+ }
+
+ public static boolean isJava(int flags) {
+ return (flags & JAVA) != 0;
+ }
+
+ public static boolean isNoVal(int flags) {
+ return (flags & PACKAGE) != 0;
+ }
+
+ public static String toString(int flags) {
+ StringBuffer buffer = new StringBuffer();
+ toString(buffer, flags);
+ return buffer.toString();
+ }
+
+ public static void toString(StringBuffer buffer, int flags) {
+ //buffer.append(flags).append(": ");//debug
+ int marker = buffer.length();
+ if (isPrivate(flags)) buffer.append("private ");
+ if (isProtected(flags)) buffer.append("protected ");
+ if (isAbstract(flags)) buffer.append("abstract ");
+ if (isFinal(flags)) buffer.append("final ");
+ if (isQualified(flags)) buffer.append("qualified ");
+ if (isInterface(flags)) buffer.append("interface ");
+ if (isCase(flags)) buffer.append("case ");
+ if (isDef(flags)) buffer.append("def ");
+ if (isOverride(flags)) buffer.append("override ");
+ int length = buffer.length();
+ buffer.setLength(length - (length == marker ? 0 : 1));
+ }
+ }
+}
diff --git a/sources/scalac/symtab/NameMangler.java b/sources/scalac/symtab/NameMangler.java
new file mode 100644
index 0000000000..30de8193d0
--- /dev/null
+++ b/sources/scalac/symtab/NameMangler.java
@@ -0,0 +1,33 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+package scalac.symtab;
+
+import scalac.util.Name;
+import java.util.HashMap;
+
+public class NameMangler {
+
+ private HashMap/*<Symbol,HashMap<Symbol,int[]>>*/ mangleMap = new HashMap();
+
+ public void setMangledName(Symbol innerclazz) {
+ Symbol topclazz = innerclazz.enclToplevelClass();
+ HashMap map = (HashMap) mangleMap.get(topclazz);
+ if (map == null) {
+ map = new HashMap();
+ mangleMap.put(topclazz, map);
+ }
+ int[] ctr = (int[]) map.get(innerclazz);
+ if (ctr == null) {
+ ctr = new int[1];
+ map.put(innerclazz, ctr);
+ }
+ innerclazz.setMangledName(
+ Name.fromString(topclazz.name + "$" + (ctr[0]++) + innerclazz.name));
+ }
+}
diff --git a/sources/scalac/symtab/Scope.java b/sources/scalac/symtab/Scope.java
new file mode 100644
index 0000000000..3bd22f51ed
--- /dev/null
+++ b/sources/scalac/symtab/Scope.java
@@ -0,0 +1,306 @@
+ /* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab;
+
+import scalac.util.*;
+import scalac.ApplicationError;
+
+public class Scope {
+
+ public static abstract class SymbolIterator {
+ public abstract boolean hasNext();
+ public abstract Symbol next();
+ }
+
+ /** A symbol iterator that returns all alternatives of an overloaded symbol
+ * instead of the overloaded symbol itself.
+ */
+ public static class UnloadIterator extends SymbolIterator {
+ private SymbolIterator iterator;
+ private Symbol[] alternatives;
+ private int index;
+
+ public UnloadIterator(SymbolIterator iterator) {
+ this.iterator = iterator;
+ this.alternatives = null;
+ this.index = -1;
+ }
+
+ public boolean hasNext() {
+ return index >= 0 || iterator.hasNext();
+ }
+ public Symbol next() {
+ if (index >= 0) {
+ Symbol symbol = alternatives[index++];
+ if (index == alternatives.length) {
+ alternatives = null;
+ index = -1;
+ }
+ return symbol;
+ } else {
+ Symbol symbol = iterator.next();
+ switch (symbol.type()) {
+ case OverloadedType(Symbol[] alts, _):
+ alternatives = alts;
+ index = 0;
+ return next();
+ default:
+ return symbol;
+ }
+ }
+ }
+ }
+
+ public static class Entry {
+
+ /** the absent entry
+ */
+ public static final Entry NONE = new Entry();
+
+ /** the symbol of the entry (this is the symbol containing the name)
+ */
+ public Symbol sym;
+
+ /** the next entry in the hash bucket
+ */
+ Entry tail;
+
+ /** the next entry in this scope
+ */
+ public Entry next;
+
+ /** The owner of the entry;
+ */
+ public Scope owner;
+
+ Entry(Symbol sym, Scope owner) {
+ if (sym == null) throw new ApplicationError();
+ this.sym = sym;
+ this.owner = owner;
+ this.next = owner.elems;
+ owner.elems = this;
+ }
+
+ private Entry() {
+ this.sym = Symbol.NONE;
+ }
+
+ public Entry setSymbol(Symbol sym) {
+ this.sym = sym;
+ owner.elemsCache = null;
+ return this;
+ }
+
+ public int hashCode() {
+ return sym.name.index;
+ }
+
+ public String toString() {
+ return sym.toString();
+ }
+ }
+
+ /** all elements of this scope
+ */
+ public Entry elems;
+
+ /** the hash table
+ */
+ private Entry[] hashtable;
+
+ /** a cache for all elements, to be used by symbol iterator.
+ */
+ private Symbol[] elemsCache = null;
+
+ /** size and mask of hash tables
+ * todo: make hashtables grow?
+ */
+ private final int HASHSIZE = 0x80;
+ private final int HASHMASK = 0x7f;
+
+ /** the threshold number of entries from which a hashtable is constructed.
+ */
+ private final int MIN_HASH = 6;
+
+ /** construct a new name space
+ */
+ public Scope() {
+ this.elems = Entry.NONE;
+ }
+
+ public Scope(Entry elems) {
+ this.elems = elems;
+ if (size() >= MIN_HASH) createHash();
+ }
+
+ public Scope(Scope base) {
+ this.elems = base.elems;
+ if (base.hashtable != null) {
+ this.hashtable = new Entry[HASHSIZE];
+ for (int i = 0; i < HASHSIZE; i++)
+ hashtable[i] = base.hashtable[i];
+ }
+ }
+
+ public Scope(Symbol[] members) {
+ this();
+ for (int i = 0; i < members.length; i++)
+ enter(members[i]);
+ }
+
+ /** the number of entries in this scope
+ */
+ int size() {
+ int s = 0;
+ for (Entry e = elems; e != Entry.NONE; e = e.next) s++;
+ return s;
+ }
+
+ private Scope enter(Entry e) {
+ elems = e;
+ elemsCache = null;
+ if (hashtable != null) {
+ int i = e.sym.name.index & HASHMASK;
+ elems.tail = hashtable[i];
+ hashtable[i] = elems;
+ } else if (size() >= MIN_HASH) {
+ createHash();
+ }
+ return this;
+ }
+
+ /** enter a symbol
+ */
+ public Scope enter(Symbol sym) {
+ return enter(new Entry(sym, this));
+ }
+
+ public Scope enterOrOverload(Symbol sym) {
+ Entry e = lookupEntry(sym.name);
+ if (e.owner == this && (sym.flags & Modifiers.PRIVATE) == 0) {
+ e.setSymbol(e.sym.overloadWith(sym));
+ return this;
+ } else {
+ return enter(sym);
+ }
+ }
+
+ private void createHash() {
+ hashtable = new Entry[HASHSIZE];
+ for (int i = 0; i < HASHSIZE; i++)
+ hashtable[i] = Entry.NONE;
+ enterInHash(elems);
+ }
+
+ private void enterInHash(Entry e) {
+ if (e != Entry.NONE) {
+ enterInHash(e.next);
+ int i = e.sym.name.index & HASHMASK;
+ e.tail = hashtable[i];
+ hashtable[i] = e;
+ }
+ }
+
+ /** remove entry
+ */
+ public void unlink(Entry e) {
+ Entry e1 = hashtable[e.sym.name.index & HASHMASK];
+ if (e1 == e) {
+ hashtable[e.sym.name.index & HASHMASK] = e.tail;
+ } else {
+ while (e1.tail != e) e1 = e1.tail;
+ }
+ if (elems == e) {
+ elems = e.next;
+ } else {
+ e1 = elems;
+ while (e1.next != e) e1 = e1.next;
+ e1.next = e.next;
+ }
+ elemsCache = null;
+ }
+
+ /** lookup a symbol
+ */
+ public Symbol lookup(Name name) {
+ return lookupEntry(name).sym;
+ }
+
+ /** lookup a symbol entry.
+ */
+ public Entry lookupEntry(Name name) {
+ Entry e;
+ if (hashtable != null) {
+ e = hashtable[name.index & HASHMASK];
+ while (e != Entry.NONE && e.sym.name != name) e = e.tail;
+ } else {
+ e = elems;
+ while (e != Entry.NONE && e.sym.name != name) e = e.next;
+ }
+ return e;
+ }
+
+ /** return all symbols as an array,
+ * in the order they were entered in this scope.
+ */
+ public Symbol[] elements() {
+ if (elemsCache == null) {
+ int s = size();
+ elemsCache = new Symbol[s];
+ for (Entry e = elems; e != Entry.NONE; e = e.next)
+ elemsCache[--s] = e.sym;
+ }
+ return elemsCache;
+ }
+
+ /** return all symbols as an iterator,
+ * in the order they were entered in this scope.
+ */
+ public SymbolIterator iterator() { return new MySymbols(); }
+
+ class MySymbols extends SymbolIterator {
+
+ private int index;
+ MySymbols() {
+ elements();
+ index = 0;
+ }
+
+ public boolean hasNext() {
+ return index < elemsCache.length;
+ }
+
+ public Symbol next() {
+ return elemsCache[index++];
+ }
+ }
+
+ public String toString() {
+ StringBuffer str = new StringBuffer("{");
+ SymbolIterator it = iterator();
+ while (it.hasNext()) {
+ str.append("\n " + it.next().defString());
+ }
+ str.append("}");
+ return str.toString();
+ }
+
+ public String simpleToString() {
+ StringBuffer str = new StringBuffer("{");
+ SymbolIterator it = iterator();
+ while (it.hasNext()) {
+ str.append("\n " + it.next().name);
+ }
+ str.append("}");
+ return str.toString();
+ }
+
+ public static Scope EMPTY = new Scope();
+}
+
diff --git a/sources/scalac/symtab/SourceCompleter.java b/sources/scalac/symtab/SourceCompleter.java
new file mode 100644
index 0000000000..1656f2029f
--- /dev/null
+++ b/sources/scalac/symtab/SourceCompleter.java
@@ -0,0 +1,53 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab;
+
+import scalac.*;
+import scalac.ast.parser.*;
+import scalac.typechecker.Analyzer;
+import java.io.*;
+
+
+public class SourceCompleter extends Type.LazyType {
+
+ /** the global compilation environment
+ */
+ protected Global global;
+ protected String filename;
+ private boolean completed = false;
+
+ public SourceCompleter(Global global, String filename) {
+ this.global = global;
+ this.filename = filename;
+ }
+
+ /** complete class symbol c by loading the class
+ */
+ public void complete(Symbol c) {
+ if (completed) {
+ c.setInfo(Type.ErrorType);
+ } else if (filename != null) {
+ try {
+ String fname = filename;
+ long msec = System.currentTimeMillis();
+ Unit unit = new Unit(global, new Sourcefile(filename, false));
+ filename = null;
+ global.PHASE.PARSER.createPhase(global).apply(unit);
+ ((Analyzer)global.PHASE.ANALYZER.createPhase(global)).lateEnter(unit, c);
+ global.operation("added " + fname + " in " +
+ (System.currentTimeMillis() - msec) + "ms");
+ } catch (IOException e) {
+ e.printStackTrace();
+ global.error("i/o error while loading " + c);
+ c.setInfo(Type.ErrorType);
+ }
+ completed = true;
+ }
+ }
+}
diff --git a/sources/scalac/symtab/SymSet.java b/sources/scalac/symtab/SymSet.java
new file mode 100644
index 0000000000..f72b9764f5
--- /dev/null
+++ b/sources/scalac/symtab/SymSet.java
@@ -0,0 +1,89 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.symtab;
+
+/** Sets of symbols, implemented as binary trees.
+ */
+public class SymSet {
+ private SymSet l, r;
+ private Symbol sym;
+
+ public SymSet() {}
+
+ private SymSet(Symbol sym, SymSet l, SymSet r) {
+ this.sym = sym; this.l = l; this.r = r;
+ }
+
+ /** Union of this set and `{sym}'.
+ */
+ public SymSet incl(Symbol sym) {
+ if (this == EMPTY) {
+ return new SymSet(sym, EMPTY, EMPTY);
+ } else if (sym == this.sym) {
+ return this;
+ } else if (sym.isLess(this.sym)) {
+ return new SymSet(this.sym, l.incl(sym), r);
+ } else {
+ assert this.sym.isLess(sym);
+ return new SymSet(this.sym, l, r.incl(sym));
+ }
+ }
+
+ /** Is `sym' an element of this set?
+ */
+ public boolean contains(Symbol sym) {
+ if (this == EMPTY) {
+ return false;
+ } else if (sym == this.sym) {
+ return true;
+ } else if (sym.isLess(this.sym)) {
+ return l.contains(sym);
+ } else {
+ assert this.sym.isLess(sym);
+ return r.contains(sym);
+ }
+ }
+
+ /** The number of elements in ths set.
+ */
+ public int size() {
+ if (this == EMPTY) {
+ return 0;
+ } else {
+ return 1 + l.size() + r.size();
+ }
+ }
+
+ /** Copy elements of this set into `ss', starting at index `from'.
+ * Return index one past last element copied.
+ */
+ public int copyToArray(Symbol[] ss, int from) {
+ if (this == EMPTY) {
+ return from;
+ } else {
+ from = l.copyToArray(ss, from);
+ ss[from] = sym;
+ return r.copyToArray(ss, from + 1);
+ }
+ }
+
+ /** Return all elements of this set as an array.
+ */
+ public Symbol[] toArray() {
+ int s = size();
+ if (s == 0) return Symbol.EMPTY_ARRAY;
+ Symbol[] ss = new Symbol[s];
+ copyToArray(ss, 0);
+ return ss;
+ }
+
+ /** The empty set.
+ */
+ public final static SymSet EMPTY = new SymSet();
+}
diff --git a/sources/scalac/symtab/Symbol.java b/sources/scalac/symtab/Symbol.java
new file mode 100644
index 0000000000..f13192848b
--- /dev/null
+++ b/sources/scalac/symtab/Symbol.java
@@ -0,0 +1,1291 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+//todo check significance of JAVA flag.
+
+package scalac.symtab;
+
+import scalac.ApplicationError;
+import scalac.Global;
+import scalac.PhaseDescriptor;
+import scalac.util.ArrayApply;
+import scalac.util.Name;
+import scalac.util.Names;
+import scalac.util.NameTransformer;
+import scalac.util.Position;
+import scalac.util.Debug;
+import scalac.symtab.classfile.*;
+
+
+public abstract class Symbol implements Modifiers, Kinds {
+
+ /** An empty symbol array */
+ public static final Symbol[] EMPTY_ARRAY = new Symbol[0];
+
+ /** An empty array of symbol arrays */
+ public static final Symbol[][] EMPTY_ARRAY_ARRAY = new Symbol[0][];
+
+ /** The error symbol */
+ public static final ErrorSymbol ERROR = new ErrorSymbol();
+
+ /** The absent symbol */
+ public static final NoSymbol NONE = new NoSymbol();
+
+// Fields -------------------------------------------------------------
+
+ /** The kind of the symbol */
+ public int kind;
+
+ /** The position of the symbol */
+ public int pos;
+
+ /** The name of the symbol */
+ public Name name;
+
+ /** The modifiers of the symbol */
+ public int flags;
+
+ /** The owner of the symbol */
+ private Symbol owner;
+
+ /** The infos of the symbol */
+ private TypeIntervalList infos = TypeIntervalList.EMPTY;
+
+// Constructors -----------------------------------------------------------
+
+ /** Generic symbol constructor */
+ public Symbol(int kind, int pos, Name name, Symbol owner, int flags) {
+ assert (!isTerm() || !name.isTypeName()) && (!isType() || name.isTypeName());
+
+ this.kind = kind;
+ this.pos = pos;
+ this.name = name;
+ this.owner = owner;
+ this.flags = flags & ~(INITIALIZED | LOCKED); // safety first
+ }
+
+ /** Return a fresh symbol with the same fields as this one.
+ */
+ public abstract Symbol cloneSymbol();
+
+ /** copy all fields to `sym'
+ */
+ public void copyTo(Symbol sym) {
+ sym.kind = kind;
+ sym.pos = pos;
+ sym.name = name;
+ sym.flags = flags;
+ sym.owner = owner;
+ sym.infos = infos;
+ }
+
+// Setters ---------------------------------------------------------------
+
+ /** Set the mangled name of this Symbol */
+ public Symbol setMangledName(Name name) {
+ throw new ApplicationError("illegal operation on " + getClass());
+ }
+
+ /** Set owner */
+ public Symbol setOwner(Symbol owner) {
+ this.owner = owner;
+ return this;
+ }
+
+ /** Set information, except if symbol is both initialized and locked.
+ */
+ public Symbol setInfo(Type info) {
+ return setInfo(info, currentPhaseId());
+ }
+
+ public Symbol setInfo(Type info, int limit) {
+ if ((flags & (INITIALIZED | LOCKED)) != (INITIALIZED | LOCKED)) {
+ if (infos == TypeIntervalList.EMPTY)
+ infos = new TypeIntervalList(TypeIntervalList.EMPTY);
+ infos.limit = limit;
+ infos.info = info;
+ }
+ return this;
+ }
+
+ /** Set type -- this is an alias for setInfo(Type info) */
+ public Symbol setType(Type info) { return setInfo(info); }
+
+ public Symbol updateInfo(Type info) {
+ // Global.instance.currentPhase.setInfo(this, info);
+ if (infos.limit <= Global.instance.currentPhase.id) {
+ infos = new TypeIntervalList(infos);
+ infos.limit = Global.instance.currentPhase.id + 1;
+ } else {
+ assert infos.limit == Global.instance.currentPhase.id + 1 : this;
+ }
+ infos.info = info;
+ return this;
+ }
+
+// Symbol classification ----------------------------------------------------
+
+ /** Does this symbol denote a type? */
+ public final boolean isType() {
+ return kind == TYPE || kind == CLASS || kind == ALIAS;
+ }
+
+ /** Does this symbol denote a term? */
+ public final boolean isTerm() {
+ return kind == VAL;
+ }
+
+ /** Does this symbol denote a value? */
+ public final boolean isValue() {
+ return kind == VAL && !(isModule() && isJava()) && !isPackage();
+ }
+
+ /** Does this symbol denote a variable? */
+ public final boolean isVariable() {
+ return kind == VAL && (flags & MUTABLE) != 0;
+ }
+
+ /** Does this symbol denote a method?
+ */
+ public final boolean isInitializedMethod() {
+ if (infos.limit < 0) return false;
+ switch (rawInfo()) {
+ case MethodType(_, _):
+ case PolyType(_, _): return true;
+ default: return false;
+ }
+ }
+
+ public final boolean isMethod() {
+ initialize();
+ return isInitializedMethod();
+ }
+
+ /* Does this symbol denote an anonymous class? */
+ public final boolean isAnonymousClass() {
+ return kind == CLASS &&
+ (name == Names.EMPTY.toTypeName() ||
+ name == Names.ANON_CLASS_NAME.toTypeName());
+ }
+
+ /** Does this symbol denote the root class or root module?
+ */
+ public final boolean isRoot() {
+ return this.moduleClass() == Global.instance.definitions.ROOT_CLASS;
+ }
+
+ /** Does this symbol denote something loaded from a Java class? */
+ public final boolean isJava() {
+ return (flags & JAVA) != 0;
+ }
+
+ /** Does this symbol denote a Java package? */
+ public final boolean isPackage() {
+ return (flags & PACKAGE) != 0;
+ }
+
+ /** Does this symbol denote a module? */
+ public final boolean isModule() {
+ return kind == VAL && (flags & MODUL) != 0;
+ }
+
+ /** Does this symbol denote a module? */
+ public final boolean isModuleClass() {
+ return kind == CLASS && (flags & MODUL) != 0;
+ }
+
+ /** Does this symbol denote a module? */
+ public final boolean isClass() {
+ return kind == CLASS;
+ }
+
+ /** Does this symbol denote a case class?
+ */
+ public final boolean isCaseClass() {
+ return kind == CLASS && (flags & CASE) != 0;
+ }
+
+ /** Does this symbol denote a uniform (i.e. parameterless) class? */
+ public final boolean isTrait() {
+ return kind == CLASS && (flags & TRAIT) != 0;
+ }
+
+ /** Does this class symbol denote a compound type symbol?
+ */
+ public final boolean isCompoundSym() {
+ return name == Names.COMPOUND_NAME.toTypeName();
+ }
+
+ /** Does this symbol denote an interface? */
+ public final boolean isInterface() {
+ return (flags & INTERFACE) != 0;
+ }
+
+ /** Does this symbol denote a static member? */
+ public final boolean isStatic() {
+ return (flags & STATIC) != 0;
+ }
+
+ /** Is this symbol locally defined? I.e. not a member of a class or module */
+ public final boolean isLocal() {
+ return owner.kind == VAL && !owner.isPrimaryConstructor();
+ }
+
+ /** Is this symbol a parameter? Includes type parameters of methods.
+ */
+ public final boolean isParameter() {
+ return (flags & PARAM) != 0;
+ }
+
+ /** Is this symbol a def parameter?
+ */
+ public final boolean isDefParameter() {
+ return (flags & (PARAM | DEF)) == (PARAM | DEF);
+ }
+
+ /** 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 class
+ */
+ public final boolean isLocalClass() {
+ return kind == CLASS &&
+ !isPackage() &&
+ (name == Names.EMPTY.toTypeName() ||
+ owner.isValue() ||
+ owner.isLocalClass());
+ }
+
+ /** Is this symbol a constructor? */
+ public final boolean isConstructor() {
+ return name.isConstrName();
+ }
+
+ /** Is this symbol the primary constructor of a type? */
+ public final boolean isPrimaryConstructor() {
+ return isConstructor() && this == constructorClass().constructor();
+ }
+
+ public boolean isGenerated() {
+ return name.pos((byte)'$') < name.length();
+ }
+
+ /** Symbol was preloaded from package
+ */
+ public boolean isPreloaded() {
+ return owner.isPackage() && pos == Position.NOPOS;
+ }
+
+// Symbol names ----------------------------------------------------------------
+
+ /** Get the fully qualified name of this Symbol
+ * (this is always a normal name, never a type name)
+ */
+ public Name fullName() {
+ return name.toTermName();
+ }
+
+ /** Get the mangled name of this Symbol
+ * (this is always a normal name, never a type name)
+ */
+ public Name mangledName() {
+ return name.toTermName();
+ }
+
+ /** Get the fully qualified mangled name of this Symbol */
+ public Name mangledFullName() {
+ return fullName().replace((byte)'.', (byte)'$');
+ }
+
+// Acess to related symbols -----------------------------------------------------
+
+ /** Get type parameters */
+ public Symbol[] typeParams() {
+ return EMPTY_ARRAY;
+ }
+
+ /** Get primary constructor of class */
+ public Symbol constructor() {
+ return NONE;
+ }
+
+ /** Get module associated with class */
+ public Symbol module() {
+ return NONE;
+ }
+
+ /** Get owner */
+ public Symbol owner() {
+ return owner;
+ }
+
+ /** Get owner, but if owner is primary constructor of a class,
+ * get class symbol instead. This is useful for type parameters
+ * and value parameters in classes which have the primary constructor
+ * as owner.
+ */
+ public Symbol classOwner() {
+ Symbol owner = owner();
+ Symbol clazz = owner.constructorClass();
+ if (clazz.constructor() == owner) return clazz;
+ else return owner;
+ }
+
+ /** The next enclosing class */
+ public Symbol enclClass() {
+ return owner().enclClass();
+ }
+
+ /** The top-level class enclosing `sym'
+ */
+ Symbol enclToplevelClass() {
+ Symbol sym = this;
+ while (sym.kind == VAL ||
+ (sym.kind == CLASS && !sym.owner().isPackage())) {
+ sym = sym.owner();
+ }
+ return sym;
+ }
+
+ /* If this is a constructor, return the class it constructs.
+ * Otherwise return the symbol itself.
+ */
+ public Symbol constructorClass() {
+ return this;
+ }
+
+ /* If this is a module, return its class.
+ * Otherwise return the symbol itself.
+ */
+ public Symbol moduleClass() {
+ return this;
+ }
+
+ /** The symbol accessed by this accessor function.
+ */
+ public Symbol accessed() {
+ assert (flags & ACCESSOR) != 0;
+ Name name1 = name;
+ if (name1.endsWith(Names._EQ))
+ name1 = name1.subName(0, name1.length() - Names._EQ.length());
+ return owner.info().lookup(Name.fromString(name1 + "$"));
+ }
+
+ /** The members of this class or module symbol
+ */
+ public Scope members() {
+ return info().members();
+ }
+
+// Symbol types --------------------------------------------------------------
+
+ /** Was symbol's type updated during phase `id'?
+ */
+ public boolean isUpdated(int id) {
+ return infos.limit >= id;
+ }
+
+ /** the current phase id, or the id after analysis, whichever is larger.
+ */
+ int currentPhaseId() {
+ int id = Global.instance.currentPhase.id;
+ if (id > Global.instance.POST_ANALYZER_PHASE_ID)
+ id = Global.instance.POST_ANALYZER_PHASE_ID;
+ return id;
+ }
+
+ /** Is this symbol initialized? */
+ public final boolean isInitialized() {
+ return (flags & INITIALIZED) != 0;
+ }
+
+ /** Initialize the symbol */
+ public final Symbol initialize() {
+ info();
+ return this;
+ }
+
+ /** Get info; This is:
+ * for a term symbol, its type
+ * for a type variable, its bound
+ * for a type alias, its right-hand side
+ * for a class symbol, the compound type consisting of
+ * its baseclasses and members.
+ */
+ public Type info() {
+ if ((flags & INITIALIZED) == 0) {
+ int id = currentPhaseId();
+ Type info = rawInfoAt(id);
+ assert info != null : this;
+
+ if ((flags & LOCKED) != 0) {
+ setInfo(Type.ErrorType);
+ flags |= INITIALIZED;
+ throw new CyclicReference(this, info);
+ }
+ flags |= LOCKED;
+ //System.out.println("completing " + this);//DEBUG
+ info.complete(this);
+ flags = flags & ~LOCKED;
+ if (info instanceof SourceCompleter && (flags & SNDTIME) == 0) {
+ flags |= SNDTIME;
+ return info();
+ } else {
+ assert !(rawInfoAt(id) instanceof Type.LazyType) : this;
+ flags |= INITIALIZED;
+ }
+ //System.out.println("done: " + this);//DEBUG
+ }
+ return rawInfoAt(Global.instance.currentPhase.id);
+ }
+
+ /** Get info at phase #id
+ */
+ public Type infoAt(int id) {
+ info();
+ return rawInfoAt(id);
+ }
+
+ /** get info at phase #id, without forcing lazy types.
+ */
+ private Type rawInfoAt(int id) {
+ int nextid = infos.limit;
+ assert infos != TypeIntervalList.EMPTY : this;
+ if (nextid < id) {
+ do {
+ Type newInfo =
+ Global.instance.phases[nextid].transformInfo(this, infos.info);
+ if (newInfo != infos.info) {
+ infos = new TypeIntervalList(infos);
+ infos.info = newInfo;
+ }
+ nextid++;
+ infos.limit = nextid;
+ } while (nextid < id);
+ return infos.info;
+ } else {
+ TypeIntervalList infos1 = infos;
+ while (infos1.prev.limit >= id) {
+ infos1 = infos1.prev;
+ }
+ return infos1.info;
+ }
+ }
+
+ public Type rawInfo() {
+ return rawInfoAt(Global.instance.currentPhase.id);
+ }
+
+ /** The type of a symbol is:
+ * for a type symbol, the type corresponding to the symbol itself
+ * for a term symbol, its usual type
+ */
+ public Type type() {
+ return info();
+ }
+
+ /** The type at phase #id
+ */
+ public Type typeAt(int id) {
+ return infoAt(id);
+ }
+
+ /** The types of these symbols as an array.
+ */
+ static public Type[] type(Symbol[] syms) {
+ Type[] tps = new Type[syms.length];
+ for (int i = 0; i < syms.length; i++)
+ tps[i] = syms[i].type();
+ return tps;
+ }
+
+ /** Get this.type corresponding to this symbol
+ */
+ public Type thisType() {
+ return Type.localThisType;
+ }
+
+ public Type typeOfThis() {
+ return Type.localThisType;
+ }
+
+ /** A total ordering between symbols that refines the class
+ * inheritance graph (i.e. subclass.isLess(superclass) always holds).
+ */
+ public boolean isLess(Symbol that) {
+ if (this == that) return false;
+ int diff;
+ if (this.isType()) {
+ if (that.isType()) {
+ diff = this.closure().length - that.closure().length;
+ if (diff > 0) return true;
+ if (diff < 0) return false;
+ } else {
+ return true;
+ }
+ } else if (that.isType()) {
+ return false;
+ }
+
+ diff = that.mangledName().index - this.mangledName().index;
+ if (diff > 0) return true;
+ if (diff < 0) return false;
+
+ diff = that.mangledFullName().index - this.mangledFullName().index;
+ if (diff > 0) return true;
+ if (diff < 0) return false;
+
+ throw new ApplicationError(
+ "Giving up: can't order two incarnations of class " +
+ this.mangledFullName());
+ }
+
+ /** Return the symbol's type itself followed by all its direct and indirect
+ * base types, sorted by isLess(). Overridden for class symbols.
+ */
+ public Type[] closure() {
+ return info().closure();
+ }
+
+ /** Return position of `c' in the closure of this type; -1 if not there.
+ */
+ public int closurePos(Symbol c) {
+ if (this == c) return 0;
+ if (c.isCompoundSym()) return -1;
+ Type[] closure = closure();
+ int lo = 0;
+ int hi = closure.length - 1;
+ while (lo <= hi) {
+ int mid = (lo + hi) / 2;
+ Symbol clsym = closure[mid].symbol();
+ if (c == clsym) return mid;
+ else if (c.isLess(clsym)) hi = mid - 1;
+ else if (clsym.isLess(c)) lo = mid + 1;
+ else throw new ApplicationError();
+ }
+ return -1;
+ }
+
+ Type baseType(Symbol sym) {
+ int i = closurePos(sym);
+ if (i >= 0) return closure()[i];
+ else return Type.NoType;
+ }
+
+ /** Is this class a subclass of `c'? I.e. does it have a type instance
+ * of `c' as indirect base class?
+ */
+ public boolean isSubClass(Symbol c) {
+ return this == c || c.kind == Kinds.ERROR || closurePos(c) >= 0;
+ }
+
+// ToString -------------------------------------------------------------------
+
+ /** String representation of symbol's simple name.
+ * Translates expansions of operators back to operator symbol. E.g.
+ * $eq => =.
+ */
+ public String nameString() {
+ return NameTransformer.decode(name).toString();
+ }
+
+ /** String representation of symbol's full name.
+ * Translates expansions of operators back to operator symbol. E.g.
+ * $eq => =.
+ */
+ public String fullNameString() {
+ return NameTransformer.decode(fullName()).toString();
+ }
+
+ public String idString() {
+ if (Global.instance.uniqid &&
+ (kind == TYPE || Global.instance.debug))
+ return "#" + Global.instance.uniqueID.id(this);
+ else return "";
+ }
+
+ /** String representation, including symbol's kind
+ * e.g., "class Foo", "function Bar".
+ */
+ public String toString() {
+ if (isRoot()) return "<root package>";
+ if (isAnonymousClass()) return "<template>";
+ String kstr = kindString();
+ String str;
+ if (kstr.length() == 0) str = fullNameString();
+ else str = kstr + " " + fullNameString();
+ return str + idString();
+ }
+
+ /** String representation of location.
+ */
+ public String locationString() {
+ if (owner.kind == CLASS && !owner.isAnonymousClass())
+ return " in " + owner;
+ else
+ return "";
+ }
+
+ /** String representation of definition.
+ */
+ public String defString() {
+ String inner;
+ if (kind == CLASS) inner = " extends ";
+ else if (kind == TYPE) inner = " <: ";
+ else if (kind == ALIAS) inner = " = ";
+ else inner = " : ";
+ return
+ (isParameter() ? "" : defKeyword() + " ") +
+ nameString() + idString() + inner +
+ (rawInfoAt(Global.instance.POST_ANALYZER_PHASE_ID)
+ instanceof Type.LazyType ? "?" : info());
+ }
+
+ public static String[] defString(Symbol[] defs) {
+ String[] strs = new String[defs.length];
+ for (int i = 0; i < defs.length; i++)
+ strs[i] = defs[i].defString();
+ return strs;
+ }
+
+ /** String representation of kind */
+ public String kindString() {
+ switch (kind) {
+ case CLASS:
+ if ((flags & TRAIT) != 0)
+ return "trait";
+ else if ((flags & MODUL) != 0 && Global.instance.debug)
+ return "module class";
+ else
+ return "class";
+ case TYPE:
+ case ALIAS:
+ return "type";
+ case VAL:
+ if (isVariable()) return "variable";
+ else if (isModule()) return "module";
+ else if (isConstructor()) return "constructor";
+ else if (isInitializedMethod()) return "method";
+ else return "value";
+ default: return "";
+ }
+ }
+
+ /** Definition keyword of kind
+ */
+ public String defKeyword() {
+ switch (kind) {
+ case CLASS: if ((flags & TRAIT) != 0) return "trait"; else return "class";
+ case TYPE:
+ case ALIAS: return "type";
+ case VAL:
+ if (isVariable()) return "var";
+ else if (isModule()) return "module";
+ else if (isInitializedMethod()) return "def";
+ else return "val";
+ default: return "";
+ }
+ }
+
+// Overloading and Overriding -------------------------------------------
+
+ /** Add another overloaded alternative to this symbol.
+ */
+ public Symbol overloadWith(Symbol that) {
+ assert isTerm() : this;
+ assert this.name == that.name : this + " " + that;
+ assert this.owner == that.owner : this + " " + that;
+ assert (this.flags & that.flags & JAVA) != 0 ||
+ (this.flags & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) ==
+ (that.flags & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) : this + " " + that;
+ TermSymbol overloaded = new TermSymbol(
+ pos, name, owner,
+ ((this.flags | that.flags) & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) |
+ (this.flags & that.flags & ACCESSFLAGS));
+ overloaded.setInfo(new LazyOverloadedType(this, that));
+ return overloaded;
+ }
+
+ /** A lazy type which, when forced computed the overloaded type
+ * of symbols `sym1' and `sym2'. It also checks that this type is well-formed.
+ */
+ private static class LazyOverloadedType extends Type.LazyType {
+ Symbol sym1;
+ Symbol sym2;
+ LazyOverloadedType(Symbol sym1, Symbol sym2) {
+ this.sym1 = sym1;
+ this.sym2 = sym2;
+ }
+ private Symbol[] alts(Symbol sym) {
+ if (sym == null) return Symbol.EMPTY_ARRAY;
+ switch (sym.type()) {
+ case OverloadedType(Symbol[] alts, _): return alts;
+ default: return new Symbol[]{sym};
+ }
+ }
+ private Type[] alttypes(Symbol sym) {
+ if (sym == null) return Type.EMPTY_ARRAY;
+ switch (sym.type()) {
+ case OverloadedType(_, Type[] alttypes): return alttypes;
+ default: return new Type[]{sym.type()};
+ }
+ }
+ public void complete(Symbol overloaded) {
+ if (sym1 != null) sym1.initialize();
+ if (sym2 != null) sym2.initialize();
+
+ Symbol[] alts1 = alts(sym1);
+ Symbol[] alts2 = alts(sym2);
+ Symbol[] alts3 = new Symbol[alts1.length + alts2.length];
+ System.arraycopy(alts1, 0, alts3, 0, alts1.length);
+ System.arraycopy(alts2, 0, alts3, alts1.length, alts2.length);
+
+ Type[] alttypes1 = alttypes(sym1);
+ Type[] alttypes2 = alttypes(sym2);
+ Type[] alttypes3 = new Type[alttypes1.length + alttypes2.length];
+ System.arraycopy(alttypes1, 0, alttypes3, 0, alttypes1.length);
+ System.arraycopy(alttypes2, 0, alttypes3, alttypes1.length, alttypes2.length);
+ overloaded.setInfo(Type.OverloadedType(alts3, alttypes3));
+ }
+ }
+
+ /** All the alternatives of this symbol if it's overloaded, the
+ * symbol alone otherwise.
+ */
+ public Symbol[] alternatives() {
+ switch (type()) {
+ case OverloadedType(Symbol[] alts, _): return alts;
+ default: return new Symbol[]{this};
+ }
+ }
+
+ /** The symbol which is overridden by this symbol in base class `base'
+ * `base' must be a superclass of this.owner().
+ */
+ public Symbol overriddenSymbol(Type base) {
+ Symbol sym1 = base.lookupNonPrivate(name);
+ if (sym1.kind == Kinds.NONE || (sym1.flags & STATIC) != 0) {
+ return Symbol.NONE;
+ } else {
+ //System.out.println(this + ":" + this.type() + locationString() + " overrides? " + sym1 + sym1.type() + sym1.locationString()); //DEBUG
+
+ Type symtype = owner.thisType().memberType(this);
+ //todo: try whether we can do: this.type(); instead
+ Type sym1type = owner.thisType().memberType(sym1);
+ switch (sym1type) {
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ for (int i = 0; i < alts.length; i++) {
+ if (symtype.isSameAs(alttypes[i])) return alts[i];
+ }
+ return Symbol.NONE;
+ default:
+ if (symtype.isSameAs(sym1type)) return sym1;
+ else {
+ System.out.println(this + locationString() + " does not override " + sym1 + sym1.locationString() + ", since " + symtype + " # " + sym1type);//DEBUG
+ return Symbol.NONE;
+ }
+ }
+ }
+ }
+}
+
+/** A class for term symbols
+ */
+public class TermSymbol extends Symbol {
+
+ private Symbol clazz;
+
+ /** Constructor */
+ public TermSymbol(int pos, Name name, Symbol owner, int flags) {
+ super(VAL, pos, name, owner, flags);
+ }
+
+ public static TermSymbol newConstructor(Symbol clazz, int flags) {
+ TermSymbol sym = new TermSymbol(
+ clazz.pos, clazz.name.toConstrName(), clazz.owner(),
+ flags | FINAL);
+ sym.clazz = clazz;
+ return sym;
+ }
+
+ public static TermSymbol newJavaConstructor(Symbol clazz) {
+ return newConstructor(clazz, clazz.flags & (ACCESSFLAGS | QUALIFIED | JAVA));
+ }
+
+ public static TermSymbol newModule(int pos, Name name, Symbol owner, int flags) {
+ TermSymbol sym = new TermSymbol(pos, name, owner, flags | MODUL | FINAL);
+ Symbol clazz = new ClassSymbol(
+ pos, name.toTypeName(), owner, flags | MODUL | FINAL, sym);
+ Type clazztype = Type.TypeRef(owner.thisType(), clazz, Type.EMPTY_ARRAY);
+ clazz.constructor().setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, clazztype));
+ sym.clazz = clazz;
+ sym.setInfo(clazztype);
+ return sym;
+ }
+
+ /** Constructor for companion modules to classes, which need to be completed.
+ */
+ public static TermSymbol newCompanionModule(Symbol clazz, int flags, Type.LazyType parser) {
+ TermSymbol sym = newModule(Position.NOPOS, clazz.name.toTermName(), clazz.owner(),
+ FINAL | flags);
+ sym.clazz.setInfo(parser);
+ return sym;
+ }
+
+ /** Java package module constructor
+ */
+ public static TermSymbol newJavaPackageModule(Name name, Symbol owner, Type.LazyType parser) {
+ TermSymbol sym = newModule(Position.NOPOS, name, owner, JAVA | PACKAGE);
+ sym.clazz.flags |= SYNTHETIC;
+ sym.clazz.setInfo(parser);
+ return sym;
+ }
+
+ /** Get this.type corresponding to this class or module
+ */
+ public Type thisType() {
+ if ((flags & MODUL) != 0) return moduleClass().thisType();
+ else return Type.localThisType;
+ }
+ /** Get the fully qualified name of this Symbol */
+ public Name fullName() {
+ if ((flags & MODUL) != 0) return moduleClass().fullName();
+ else return super.fullName();
+ }
+
+ /** Return a fresh symbol with the same fields as this one.
+ */
+ public Symbol cloneSymbol() {
+ assert !isPrimaryConstructor() : Debug.show(this);
+ TermSymbol other;
+ if (isModule()) {
+ other = newModule(pos, name, owner(), flags);
+ } else {
+ other = new TermSymbol(pos, name, owner(), flags);
+ other.clazz = clazz;
+ }
+ other.setInfo(info());
+ return other;
+ }
+
+ public Symbol constructorClass() {
+ return isConstructor() ? clazz : this;
+ }
+
+ public Symbol moduleClass() {
+ return (flags & MODUL) != 0 ? clazz : this;
+ }
+}
+
+/** A class for (abstract and alias) type symbols. It has ClassSymbol as a subclass.
+ */
+public class TypeSymbol extends Symbol {
+
+ /** A cache for closures
+ */
+ private ClosureIntervalList closures = ClosureIntervalList.EMPTY;
+
+ /** The symbol's type template */
+ private Type template;
+
+ /** The primary constructor of this type */
+ public final Symbol constructor;
+
+ /** Constructor */
+ public TypeSymbol(int kind, int pos, Name name, Symbol owner, int flags) {
+ super(kind, pos, name, owner, flags);
+ this.constructor = TermSymbol.newConstructor(this, flags);
+ if (kind == TYPE) { // provide a constructor type
+ this.constructor().setInfo(
+ Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType));
+ }
+ }
+
+ /** Return a fresh symbol with the same fields as this one.
+ */
+ public Symbol cloneSymbol() {
+ if (Global.instance.debug) System.out.println("cloning " + this + this.locationString());
+ TypeSymbol other = new TypeSymbol(kind, pos, name, owner(), flags);
+ other.setInfo(info());
+ other.constructor.setInfo(constructor.info());
+ return other;
+ }
+
+ /** Get self type */
+ public Type type() {
+ if (template == null || template.typeArgs().length != typeParams().length) {
+ template = Type.TypeRef(
+ owner().thisType(), this, type(typeParams()));
+ }
+ return template;
+ }
+
+ //todo: needed?
+ public Type typeAt(int id) {
+ return type();
+ }
+
+ /** Get type parameters */
+ public Symbol[] typeParams() {
+ return constructor.info().typeParams();
+ }
+
+ /** Get primary constructor */
+ public Symbol constructor() {
+ return constructor;
+ }
+
+ public Type[] closure() {
+ if (kind == ALIAS) return info().symbol().closure();
+ int id = Global.instance.currentPhase.id;
+ if (closures.limit < id) {
+ if (id == 0 || changes(closureAt(id - 1))) {
+ closures = new ClosureIntervalList(closures);
+ closures.limit = id;
+ computeClosure();
+ } else {
+ closures.limit = id;
+ }
+ return closures.closure;
+ } else {
+ ClosureIntervalList closures1 = closures;
+ while (closures1.prev.limit >= id) {
+ closures1 = closures1.prev;
+ }
+ return closures1.closure;
+ }
+ }
+
+ //todo: needed?
+ private Type[] closureAt(int id) {
+ PhaseDescriptor savedPhase = Global.instance.currentPhase;
+ Global.instance.currentPhase = Global.instance.phases[id];
+ Type[] c = closure();
+ Global.instance.currentPhase = savedPhase;
+ return c;
+ }
+
+ private boolean changes(Type[] closure) {
+ for (int i = 0; i < closure.length; i++) {
+ Symbol c = closure[i].symbol();
+ if (c.infoAt(Global.instance.currentPhase.id - 1) != c.info())
+ return true;
+ }
+ return false;
+ }
+
+ private static Type[] BAD_CLOSURE = new Type[0];
+
+ /** Return the type itself followed by all direct and indirect
+ * base types of this type, sorted by isLess().
+ */
+ private void computeClosure() {
+ assert closures.closure != BAD_CLOSURE : this;
+ closures.closure = BAD_CLOSURE; // to catch cycles.
+ SymSet closureClassSet = inclClosureBases(SymSet.EMPTY, this);
+ Symbol[] closureClasses = new Symbol[closureClassSet.size() + 1];
+ closureClasses[0] = this;
+ closureClassSet.copyToArray(closureClasses, 1);
+ //System.out.println(ArrayApply.toString(closureClasses));//DEBUG
+ closures.closure = Symbol.type(closureClasses);
+ //System.out.println(ArrayApply.toString(closures.closure));//DEBUG
+ adjustType(type());
+ //System.out.println("closure(" + this + ") at " + Global.instance.currentPhase.name() + " = " + ArrayApply.toString(closures.closure));//DEBUG
+ }
+ //where
+
+ private SymSet inclClosureBases(SymSet set, Symbol c) {
+ Type[] parents = c.type().parents();
+ for (int i = 0; i < parents.length; i++) {
+ set = inclClosure(set, parents[i].symbol());
+ }
+ return set;
+ }
+
+ private SymSet inclClosure(SymSet set, Symbol c) {
+ Symbol c1 = c;
+ while (c1.kind == ALIAS) c1 = c1.info().symbol();
+ return inclClosureBases(set.incl(c1), c1);
+ }
+
+ void adjustType(Type tp) {
+ Type tp1 = tp.unalias();
+ int pos = closurePos(tp1.symbol());
+ assert pos >= 0 : this + " " + tp1 + " " + tp1.symbol();
+ closures.closure[pos] = tp1;
+ Type[] parents = tp1.parents();
+ for (int i = 0; i < parents.length; i++) {
+ adjustType(parents[i]);
+ }
+ }
+}
+
+/** A class for class symbols. It has JavaClassSymbol as a subclass.
+ */
+public class ClassSymbol extends TypeSymbol {
+
+ /** The mangled class name */
+ private Name mangled;
+
+ /** The module belonging to the class. This means:
+ * For Java classes, its statics parts.
+ * For module classes, the corresponding module.
+ * For other classes, null.
+ */
+ private Symbol module = NONE;
+
+ /** Principal Constructor for module classes and classes with static members.
+ */
+ public ClassSymbol(int pos, Name name, Symbol owner, int flags) {
+ super(CLASS, pos, name, owner, flags);
+ this.mangled = name;
+ }
+
+ /** Constructor for module classes and classes with static members.
+ */
+ public ClassSymbol(int pos, Name name, Symbol owner, int flags, Symbol module) {
+ this(pos, name, owner, flags);
+ this.module = module;
+ }
+
+ /** Constructor for classes to load as source files
+ */
+ public ClassSymbol(Name name, Symbol owner, SourceCompleter parser) {
+ this(Position.NOPOS, name, owner, 0);
+ this.module = TermSymbol.newCompanionModule(this, 0, parser);
+ this.mangled = name;
+ this.setInfo(parser);
+ }
+
+ /** Constructor for classes to load as class files.
+ */
+ public ClassSymbol(Name name, Symbol owner, ClassParser parser) {
+ super(CLASS, Position.NOPOS, name, owner, JAVA);
+ this.module = TermSymbol.newCompanionModule(this, JAVA, parser.staticsParser(this));
+ this.mangled = name;
+ this.setInfo(parser);
+ }
+
+ /** Return a fresh symbol with the same fields as this one.
+ */
+ public Symbol cloneSymbol() {
+ ClassSymbol other = new ClassSymbol(pos, name, owner(), flags);
+ other.setInfo(info());
+ other.constructor.setInfo(constructor.info());
+ other.mangled = mangled;
+ other.module = module;
+ return other;
+ }
+
+ /** Get module */
+ public Symbol module() {
+ return module;
+ }
+
+ /** Set the mangled name of this Symbol */
+ public Symbol setMangledName(Name name) {
+ this.mangled = name;
+ return this;
+ }
+
+ /** Get the fully qualified name of this Symbol */
+ public Name fullName() {
+ if (owner().kind == CLASS && owner().name.length() != 0)
+ return Name.fromString(owner().fullName() + "." + name);
+ else
+ return name.toTermName();
+ }
+
+ /** Get the mangled name of this Symbol */
+ public Name mangledName() {
+ return mangled;
+ }
+
+ /** Get the fully qualified mangled name of this Symbol */
+ public Name mangledFullName() {
+ if (mangled == name) {
+ return fullName().replace((byte)'.', (byte)'$');
+ } else {
+ return Name.fromString(
+ enclToplevelClass().mangledFullName() + "$" + mangled);
+ }
+ }
+
+ private Type thistp = Type.ThisType(this);
+
+ public Type thisType() {
+ return thistp;
+ }
+
+ /** Return the next enclosing class */
+ public Symbol enclClass() {
+ return this;
+ }
+
+ public Symbol caseFieldAccessor(int index) {
+ assert (flags & CASE) != 0 : this;
+ Scope.SymbolIterator it = info().members().iterator();
+ Symbol sym = null;
+ for (int i = 0; i <= index; i++) {
+ do {
+ sym = it.next();
+ } while (sym.kind != VAL || (sym.flags & CASE) == 0);
+ }
+ //System.out.println(this + ", case field[" + index + "] = " + sym);//DEBUG
+ assert sym != null : this;
+ return sym;
+ }
+}
+
+/** A class for error symbols.
+ */
+public final class ErrorSymbol extends Symbol {
+
+ /** Constructor */
+ public ErrorSymbol() {
+ super(Kinds.ERROR, Position.NOPOS, Name.fromString("<error>"), null,
+ INITIALIZED);
+ this.setOwner(this);
+ this.setInfo(Type.ErrorType);
+ }
+
+ public Symbol cloneSymbol() {
+ return this;
+ }
+
+ /** Set the mangled name of this Symbol */
+ public Symbol mangled(Name name) {
+ return this;
+ }
+
+ /** Set owner */
+ public Symbol setOwner(Symbol owner) {
+ if (owner != this)
+ throw new ApplicationError("illegal operation on " + getClass());
+ return super.setOwner(owner);
+ }
+
+ /** Set type */
+ public Symbol setInfo(Type info) {
+ if (info != Type.ErrorType)
+ throw new ApplicationError("illegal operation on " + getClass());
+ return super.setInfo(info);
+ }
+
+ /** Get primary constructor */
+ public Symbol constructor() {
+ return TermSymbol.newConstructor(this, 0).setInfo(Type.ErrorType);
+ }
+
+ /** Return the next enclosing class */
+ public Symbol enclClass() {
+ return this;
+ }
+}
+
+/** The class of Symbol.NONE
+ */
+public final class NoSymbol extends Symbol {
+
+ /** Constructor */
+ public NoSymbol() {
+ super(Kinds.NONE, Position.NOPOS, Name.fromString("<none>"), null, INITIALIZED);
+ this.setInfo(Type.NoType);
+ this.setOwner(this);
+ }
+
+ /** Return a fresh symbol with the same fields as this one.
+ */
+ public Symbol cloneSymbol() {
+ return this;
+ }
+
+ /** Set the mangled name of this Symbol */
+ public Symbol mangled(Name name) {
+ throw new ApplicationError("illegal operation on " + getClass());
+ }
+
+ /** Set owner */
+ public Symbol setOwner(Symbol owner) {
+ if (owner != this)
+ throw new ApplicationError("illegal operation on " + getClass());
+ return super.setOwner(owner);
+ }
+
+ /** Set type */
+ public Symbol setInfo(Type info) {
+ if (info != Type.NoType)
+ throw new ApplicationError("illegal operation on " + getClass());
+ return super.setInfo(info);
+ }
+
+ /** Return the next enclosing class */
+ public Symbol enclClass() {
+ return this;
+ }
+
+ public Symbol owner() {
+ throw new ApplicationError();
+ }
+}
+
+/** A class for symbols generated in label definitions.
+ */
+public class LabelSymbol extends TermSymbol {
+
+ /** give as argument the symbol of the function that triggered
+ the creation of this label */
+ public LabelSymbol(Symbol f) {
+ super(f.pos, f.name, f, LABEL);
+ }
+}
+
+/** An exception for signalling cyclic references.
+ */
+public class CyclicReference extends Type.Error {
+ public Symbol sym;
+ public Type info;
+ public CyclicReference(Symbol sym, Type info) {
+ super("illegal cyclic reference involving " + sym);
+ this.sym = sym;
+ this.info = info;
+ }
+}
+
+/** A class for types indexed by phase numbers.
+ */
+class TypeIntervalList {
+ int limit;
+ Type info;
+ TypeIntervalList prev;
+ TypeIntervalList(TypeIntervalList prev) {
+ this.prev = prev;
+ }
+ static TypeIntervalList EMPTY = new TypeIntervalList(null);
+
+ static {
+ EMPTY.limit = -1;
+ }
+}
+
+/** A class for closures indexed by phase numbers.
+ */
+class ClosureIntervalList {
+ int limit;
+ Type[] closure;
+ ClosureIntervalList prev;
+ ClosureIntervalList(ClosureIntervalList prev) {
+ this.prev = prev;
+ }
+ static ClosureIntervalList EMPTY = new ClosureIntervalList(null);
+ static {
+ EMPTY.limit = -1;
+ }
+}
+
diff --git a/sources/scalac/symtab/Type.java b/sources/scalac/symtab/Type.java
new file mode 100644
index 0000000000..3ec37799f4
--- /dev/null
+++ b/sources/scalac/symtab/Type.java
@@ -0,0 +1,2353 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+//todo: implement rule single
+
+package scalac.symtab;
+
+import scalac.ApplicationError;
+import scalac.util.*;
+import scalac.Global;
+
+public class Type implements Modifiers, Kinds, TypeTags {
+
+ public static boolean debugSwitch = false;
+ private static int indent = 0;
+
+ //todo: convert C with {} to C.
+
+ public case ErrorType; // not used after analysis
+ public case AnyType; // not used after analysis
+ public case NoType;
+
+ public case ThisType(Symbol sym);
+
+ public case TypeRef(Type pre, Symbol sym, Type[] args);
+
+ public case SingleType(Type pre, Symbol sym) {
+ assert this instanceof ExtSingleType;
+ }
+
+ public case CompoundType(Type[] parts, Scope members) {
+ assert this instanceof ExtCompoundType;
+ }
+ public case MethodType(Symbol[] vparams, Type result);
+ public case PolyType(Symbol[] tparams, Type result);
+ public case OverloadedType(Symbol[] alts, Type[] alttypes);
+
+ /** Type such as +T. Only used as type arguments.
+ */
+ public case CovarType(Type tp) {
+ assert !(tp instanceof CovarType);
+ }
+
+ /** Hidden case to implement delayed evaluation of types.
+ * No need to pattern match on this type; it will never come up.
+ */
+ public case LazyType();
+
+ /** Hidden case to implement local type inference.
+ * Later phases do not need to match on this type.
+ */
+ public case TypeVar(Type origin, Constraint constr);
+
+ /** Hidden cases to implement type erasure.
+ * Earlier phases do not need to match on these types.
+ */
+ public case UnboxedType(int tag);
+ public case UnboxedArrayType(Type elemtp);
+
+ /** Force evaluation of a lazy type. No cycle
+ * check is needed; since this is done in Symbol.
+ * @see Symbol.info().
+ */
+ public void complete(Symbol p) {}
+
+// Creators ---------------------------------------------------------------------
+
+ /** An owner-less ThisType
+ */
+ public static Type localThisType = ThisType(Symbol.NONE);
+
+ /** An empty Type array */
+ public static final Type[] EMPTY_ARRAY = new Type[0];
+
+ /** A non-existing Type array; used to express type erasure */
+ public static final Type[] NO_ARRAY = new Type[0];
+
+ public static SingleType singleType(Type pre, Symbol sym) {
+ return new ExtSingleType(pre, sym);
+ }
+
+ public static TypeRef appliedType(Type tycon, Type[] args) {
+ switch (tycon) {
+ case TypeRef(Type pre, Symbol sym, Type[] args1):
+ if (args == args1) return (TypeRef)tycon;
+ else return TypeRef(pre, sym, args);
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ public static CovarType covarType(Type tp) {
+ if (tp instanceof CovarType) return (CovarType)tp;
+ else return CovarType(tp);
+ }
+
+ public static CompoundType compoundType(Type[] parts, Scope members,
+ Symbol clazz) {
+ ExtCompoundType res = new ExtCompoundType(parts, members);
+ res.tsym = clazz;
+ return res;
+ }
+
+ public static CompoundType compoundType(Type[] parts, Scope members) {
+ ExtCompoundType res = new ExtCompoundType(parts, members);
+ res.tsym = new ClassSymbol(
+ Position.NOPOS, Names.COMPOUND_NAME.toTypeName(), Symbol.NONE,
+ SYNTHETIC | ABSTRACTCLASS);
+ res.tsym.setInfo(res);
+ res.tsym.constructor().setInfo(
+ Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType));
+ return res;
+ }
+
+ static class ExtSingleType extends SingleType {
+ Type tp = null;
+ int definedId = -1;
+ ExtSingleType(Type pre, Symbol sym) {
+ super(pre, sym);
+ }
+ public Type widen() {
+ if (definedId != Global.instance.currentPhase.id) {
+ definedId = Global.instance.currentPhase.id;
+ tp = pre.memberType(sym).widen();
+ }
+ return tp;
+ }
+ }
+
+ static class ExtCompoundType extends CompoundType {
+ Symbol tsym;
+ ExtCompoundType(Type[] parts, Scope members) {
+ super(parts, members);
+ }
+ public Symbol symbol() {
+ return tsym;
+ }
+ void validate() {//debug
+ for (Scope.Entry e = members.elems; e != Scope.Entry.NONE; e = e.next)
+ assert e.sym.owner() == tsym;
+ }
+ }
+
+ void validate() {//debug
+ }
+
+// Access methods ---------------------------------------------------------------
+
+ /** If this is a thistype, named type, applied type, singleton type, or compound type,
+ * its symbol, otherwise Symbol.NONE.
+ */
+ public Symbol symbol() {
+ switch (this) {
+ case ErrorType:
+ return Symbol.ERROR;
+ case ThisType(Symbol sym):
+ return sym;
+ case TypeRef(_, Symbol sym, _):
+ return sym;
+ case SingleType(_, Symbol sym):
+ return sym;
+ case TypeVar(Type origin, _):
+ return origin.symbol();
+ case CompoundType(_, _):
+ // overridden in ExtCompoundType
+ throw new ApplicationError();
+ default:
+ return Symbol.NONE;
+ }
+ }
+
+ public static Symbol[] symbol(Type[] tps) {
+ Symbol[] syms = new Symbol[tps.length];
+ for (int i = 0; i < syms.length; i++)
+ syms[i] = tps[i].symbol();
+ return syms;
+ }
+
+ /** If this type is a thistype or singleton type, its underlying object type,
+ * otherwise the type itself.
+ */
+ public Type widen() {
+ switch (this) {
+ case ThisType(Symbol sym):
+ return sym.type();
+ case SingleType(Type pre, Symbol sym):
+ // overridden in ExtSingleType
+ throw new ApplicationError();
+ case TypeVar(Type origin, Constraint constr):
+ if (constr.inst != NoType) return constr.inst.widen();
+ else return this;
+ default:
+ return this;
+ }
+ }
+
+ public static Type[] widen(Type[] tps) {
+ if (tps.length == 0) return Type.EMPTY_ARRAY;
+ Type[] tps1 = new Type[tps.length];
+ for (int i = 0; i < tps1.length; i++) {
+ tps1[i] = tps[i].widen();
+ assert !(tps1[i] instanceof SingleType) : tps[i];//debug
+ }
+ return tps1;
+ }
+
+ /** The thistype or singleton type corresponding to values of this type.
+ */
+ public Type narrow() {
+ switch (this) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (sym.kind == ALIAS) return pre.memberInfo(sym).narrow();
+ else if (sym.kind == CLASS) return sym.thisType();
+ else return ThisType(sym);
+ case CompoundType(_, _):
+ return symbol().thisType();
+ default:
+ return this;
+ }
+ }
+
+ /** The this is a this-type, named-type, applied type or single-type, its prefix,
+ * otherwise NoType.
+ */
+ public Type prefix() {
+ switch (this) {
+ case ThisType(Symbol sym): return sym.owner().thisType();
+ case TypeRef(Type pre, _, _): return pre;
+ case SingleType(Type pre, _): return pre;
+ case TypeVar(Type origin, Constraint constr):
+ if (constr.inst != NoType) return constr.inst.prefix();
+ else return NoType;
+ default: return NoType;
+ }
+ }
+
+ /** Get all type arguments of this type.
+ */
+ public Type[] typeArgs() {
+ switch (unalias()) {
+ case TypeRef(_, _, Type[] args):
+ return args;
+ default:
+ return Type.EMPTY_ARRAY;
+ }
+ }
+
+ /** Remove all aliases
+ */
+ public Type unalias() {
+ Type result = unalias(0);//debug
+ //if (this != result) System.out.println(this + " ==> " + result);//DEBUG
+ return result;
+ }
+
+ private Type unalias(int n) {
+ if (n == 20) throw new Type.Error("recursive type alias: " + this);
+ switch (this) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (sym.kind == ALIAS) {
+ return pre.memberInfo(sym).subst(sym.typeParams(), args).unalias(n + 1);
+ }
+ break;
+ case TypeVar(Type origin, Constraint constr):
+ if (constr.inst != NoType) return constr.inst.unalias(n + 1);
+ else return this;
+ }
+ return this;
+ }
+
+ /** The (prefix-adapted) parents of this type.
+ */
+ public Type[] parents() {
+ switch (unalias()) {
+ case ThisType(_):
+ case SingleType(_, _):
+ return widen().parents();
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (sym.kind == ALIAS)
+ return unalias().parents();
+ else if (sym.kind == CLASS) {
+ assert sym.typeParams().length == args.length : sym + " " + ArrayApply.toString(args);//debug
+ return subst(asSeenFrom(sym.info().parents(), pre, sym.owner()),
+ sym.typeParams(), args);
+ } else
+ return new Type[]{sym.info().asSeenFrom(pre, sym.owner())};
+ case CompoundType(Type[] parts, _):
+ return parts;
+ default:
+ return Type.EMPTY_ARRAY;
+ }
+ }
+
+ /** Get type parameters of polymorphic method
+ * or EMPTY_ARRAY if not applicable.
+ */
+ public Symbol[] typeParams() {
+ switch (this) {
+ case PolyType(Symbol[] tparams, _):
+ return tparams;
+ case TypeRef(_, Symbol sym, _):
+ if (sym.kind == CLASS || sym.kind == ALIAS) return sym.typeParams();
+ break;
+ }
+ return Symbol.EMPTY_ARRAY;
+ }
+
+ /** If this type is a (possibly polymorphic) method type, its result type
+ * after applying all method argument sections,
+ * otherwise the type itself.
+ */
+ public Type resultType() {
+ switch (this) {
+ case PolyType(_, Type tpe):
+ return tpe.resultType();
+ case MethodType(_, Type tpe):
+ return tpe.resultType();
+ default:
+ return this;
+ }
+ }
+
+ /** The number of value parameter sections of this type.
+ */
+ public int paramSectionCount() {
+ switch (this) {
+ case PolyType(_, Type restpe):
+ return restpe.paramSectionCount();
+ case MethodType(_, Type restpe):
+ return restpe.paramSectionCount() + 1;
+ default: return 0;
+ }
+ }
+
+ /** The arity of the first parameter section of this type.
+ */
+ public Symbol[] firstParams() {
+ switch (this) {
+ case PolyType(_, Type restpe):
+ return restpe.firstParams();
+ case MethodType(Symbol[] params, _):
+ return params;
+ default: return Symbol.EMPTY_ARRAY;
+ }
+ }
+
+ /** If this type is overloaded, its alternative types,
+ * otherwise an array consisting of this type itself.
+ */
+ public Type[] alternatives() {
+ switch (this) {
+ case OverloadedType(_, Type[] alttypes):
+ return alttypes;
+ default:
+ return new Type[]{this};
+ }
+ }
+
+ /** If type is a this type of a module class, transform to singletype of
+ * module.
+ */
+ public Type expandModuleThis() {
+ switch (this) {
+ case ThisType(Symbol sym):
+ if ((sym.flags & MODUL) != 0 && sym.module() != Symbol.NONE) {
+ return singleType(
+ sym.owner().thisType().expandModuleThis(), sym.module());
+ }
+ }
+ return this;
+ }
+
+ /** If this is a covariant type, its underlying type, otherwise the type itself.
+ */
+ public Type dropVariance() {
+ switch (this) {
+ case CovarType(Type tp): return tp;
+ default: return this;
+ }
+ }
+
+ public static Map dropVarianceMap = new Map() {
+ public Type apply(Type t) { return t.dropVariance(); }
+ };
+
+// Tests --------------------------------------------------------------------
+
+ /** Is this type a this type or singleton type?
+ */
+ public boolean isStable() {
+ switch (unalias()) {
+ case ThisType(_):
+ case SingleType(_, _):
+ case ErrorType:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** Is this type a reference to an object type?
+ */
+ public boolean isObjectType() {
+ switch (unalias()) {
+ case ThisType(_):
+ case SingleType(_, _):
+ case CompoundType(_, _):
+ return true;
+ case TypeRef(Type pre, Symbol sym, _):
+ return sym.kind != ALIAS || unalias().isObjectType();
+ default:
+ return false;
+ }
+ }
+
+ /** Is this type a covariant type?
+ */
+ public boolean isCovarType() {
+ switch (this) {
+ case CovarType(_):
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** Is this type of the form scala.Tuple_N[+T_0, ..., +T_N-1]?
+ */
+ public boolean isTupleType() {
+ switch (this) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (sym.owner() == Global.instance.definitions.SCALA_CLASS &&
+ sym.name.startsWith(Names.Tuple)) {
+ for (int i = 0; i < args.length; i++)
+ if (!args[i].isCovarType()) return false;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Is this type of the form scala.FunctionN[T_1, ..., T_n, +T]?
+ */
+ public boolean isFunctionType() {
+ switch (this) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (sym.fullName().startsWith(Names.Function))
+ for (int i = 0; i < args.length - 1; i++)
+ if (args[i].isCovarType()) return false;
+ return args.length > 0 && args[args.length - 1].isCovarType();
+ default:
+ return false;
+ }
+ }
+
+// Members and Lookup -------------------------------------------------------
+
+ /** Get the scope containing the local members of this type.
+ * Symbols in this scope are not prefix-adapted!
+ */
+ public Scope members() {
+ switch (this) {
+ case ErrorType:
+ return new Scope();
+ case TypeRef(_, Symbol sym, _):
+ return sym.info().members();
+ case SingleType(_, Symbol sym):
+ return widen().members();
+ case CompoundType(Type[] basetypes, Scope members):
+ return members;
+ default:
+ return Scope.EMPTY;
+ }
+ }
+
+ /** Lookup symbol with given name among all local and inherited members
+ * of this type; return Symbol.NONE if not found.
+ */
+ public Symbol lookup(Name name) {
+ switch (this) {
+ case ErrorType:
+ return Symbol.ERROR;
+ case ThisType(_):
+ case SingleType(_, _):
+ return widen().lookup(name);
+ case TypeRef(_, Symbol sym, _):
+ return sym.info().lookup(name);
+ case CompoundType(Type[] parts, Scope members):
+ Symbol sym = members.lookup(name);
+ if (sym.kind != NONE) return sym;
+ else return lookupNonPrivate(name);
+ default:
+ return Symbol.NONE;
+ }
+ }
+
+ /** Lookup non-private symbol with given name among all local and
+ * inherited members of this type; return Symbol.NONE if not found.
+ */
+ public Symbol lookupNonPrivate(Name name) {
+ return lookupNonPrivate(name, 0);
+ }
+
+ /** Same as before, but with additional parameter `start'.
+ * If start == 0, lookup in all basetypes of a compound type.
+ * If start == 1, lookup only in mixin classes.
+ */
+ private Symbol lookupNonPrivate(Name name, int start) {
+ switch (this) {
+ case ErrorType:
+ return Symbol.ERROR;
+ case ThisType(_):
+ case SingleType(_, _):
+ return widen().lookupNonPrivate(name);
+ case TypeRef(_, Symbol sym, _):
+ return sym.info().lookupNonPrivate(name, start);
+ case CompoundType(Type[] parts, Scope members):
+ Symbol sym = members.lookup(name);
+ if (sym.kind != NONE && (sym.flags & PRIVATE) == 0)
+ return sym;
+
+ // search base types in reverse; non-abstract members
+ // take precedence over abstract ones.
+ int i = parts.length;
+ sym = Symbol.NONE;
+ while (i > start && (sym.kind == NONE || (sym.flags & ABSTRACT) != 0)) {
+ i--;
+ Symbol sym1 = parts[i].lookupNonPrivate(name, i == 0 ? 0 : 1);
+ if (sym1.kind != NONE &&
+ (sym1.flags & PRIVATE) == 0 &&
+ (sym.kind == NONE || (sym1.flags & ABSTRACT) == 0))
+ sym = sym1;
+ }
+ return sym;
+ default:
+ return Symbol.NONE;
+ }
+ }
+
+// Maps --------------------------------------------------------------------------
+
+ /** The type of type-to-type functions.
+ */
+ public abstract static class Map {
+
+ boolean covariantOK = true;
+
+ public abstract Type apply(Type t);
+
+ /** Apply map to all top-level components of this type.
+ */
+ public Type map(Type tp) {
+ switch (tp) {
+ case ErrorType:
+ case AnyType:
+ case NoType:
+ case UnboxedType(_):
+ case TypeVar(_, _):
+ case ThisType(_):
+ return tp;
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ Type pre1 = map(pre);
+ Type[] args1 = map(args);
+ if (pre1 == pre && args1 == args) return tp;
+ else return TypeRef(pre1, sym, args1);
+ case SingleType(Type pre, Symbol sym):
+ Type pre1 = map(pre);
+ if (pre1 == pre) return tp;
+ else return singleType(pre1, sym);
+ case CompoundType(Type[] parts, Scope members):
+ Type[] parts1 = map(parts);
+ Scope members1 = map(members);
+ if (parts1 == parts && members1 == members) {
+ return tp;
+ } else {
+ Scope members2 = new Scope();
+ Type tp1 = (tp.symbol().isCompoundSym()) ? compoundType(parts1, members2)
+ : compoundType(parts1, members2, tp.symbol());
+ Symbol[] syms1 = members1.elements();
+ Symbol[] syms2 = new Symbol[syms1.length];
+ for (int i = 0; i < syms2.length; i++) {
+ syms2[i] = syms1[i].cloneSymbol().setOwner(tp1.symbol());
+ }
+ for (int i = 0; i < syms2.length; i++) {
+ syms2[i].setInfo(syms1[i].info().subst(syms1, syms2));
+ }
+ for (int i = 0; i < syms2.length; i++) {
+ members2.enter(syms2[i]);
+ }
+ return tp1;
+ }
+
+ case MethodType(Symbol[] vparams, Type result):
+ covariantOK = false;
+ Symbol[] vparams1 = map(vparams);
+ covariantOK = true;
+ Type result1 = apply(result);
+ if (vparams1 == vparams && result1 == result) return tp;
+ else return MethodType(vparams1, result1);
+ case PolyType(Symbol[] tparams, Type result):
+ covariantOK = false;
+ Symbol[] tparams1 = map(tparams);
+ covariantOK = true;
+ Type result1 = apply(result);
+ if (tparams1 != tparams) result1 = result1.subst(tparams, tparams1);
+ if (tparams1 == tparams && result1 == result) return tp;
+ else return PolyType(tparams1, result1);
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ Type[] alttypes1 = map(alttypes);
+ if (alttypes1 == alttypes) return tp;
+ else return OverloadedType(alts, alttypes1);
+ case CovarType(Type t):
+ Type t1 = apply(t);
+ if (t1 == t) return tp;
+ else return covarType(t1);
+ case UnboxedArrayType(Type elemtp):
+ Type elemtp1 = apply(elemtp);
+ if (elemtp1 == elemtp) return tp;
+ else return UnboxedArrayType(elemtp1);
+ default:
+ throw new ApplicationError(tp);
+ }
+ }
+
+ public Symbol map(Symbol sym) {
+ Type tp = sym.info();
+ Type tp1 = apply(tp);
+ if (tp == tp1) return sym;
+ else return sym.cloneSymbol().setInfo(tp1);
+ }
+
+ public Type[] map(Type[] tps) {
+ Type[] tps1 = tps;
+ for (int i = 0; i < tps.length; i++) {
+ Type tp = tps[i];
+ Type tp1 = apply(tp);
+ if (tp1 != tp && tps1 == tps) {
+ tps1 = new Type[tps.length];
+ System.arraycopy(tps, 0, tps1, 0, i);
+ }
+ tps1[i] = tp1;
+ }
+ return tps1;
+ }
+
+ /** Apply map to all elements of this array of symbols,
+ * preserving recursive references to symbols in the array.
+ */
+ public Symbol[] map(Symbol[] syms) {
+ Symbol[] syms1 = syms;
+ for (int i = 0; i < syms.length; i++) {
+ Symbol sym = syms[i];
+ Symbol sym1 = map(sym);
+ if (sym != sym1 && syms1 == syms) {
+ syms1 = new Symbol[syms.length];
+ System.arraycopy(syms, 0, syms1, 0, i);
+ }
+ syms1[i] = sym1;
+ }
+ if (syms1 != syms) {
+ for (int i = 0; i < syms1.length; i++) {
+ if (syms1[i] == syms[i])
+ syms1[i] = syms[i].cloneSymbol();
+ }
+ for (int i = 0; i < syms1.length; i++) {
+ syms1[i].setInfo(syms1[i].info().subst(syms, syms1));
+ }
+ }
+ return syms1;
+ }
+
+ /** Apply map to all elements of this array of this scope.
+ */
+ public Scope map(Scope s) {
+ Symbol[] members = s.elements();
+ Symbol[] members1 = map(members);
+ if (members == members1) return s;
+ else return new Scope(members1);
+ }
+ }
+
+// baseType and asSeenFrom --------------------------------------------------------
+
+ /** Return the base type of this type whose symbol is `clazz', or NoType, if
+ * such a type does not exist.
+ */
+ public Type baseType(Symbol clazz) {
+ //System.out.println(this + ".baseType(" + clazz + ")");//DEBUG
+ switch (this) {
+ case ErrorType:
+ return ErrorType;
+
+ case ThisType(_):
+ case SingleType(_, _):
+ return widen().baseType(clazz);
+
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (sym == clazz)
+ return this;
+ else if (sym.kind == TYPE)
+ return pre.memberInfo(sym).baseType(clazz);
+ else if (sym.kind == ALIAS)
+ return pre.memberInfo(sym).baseType(clazz)
+ .subst(sym.typeParams(), args);
+ else if (clazz.isCompoundSym())
+ return NoType;
+ else
+ return sym.baseType(clazz)
+ .asSeenFrom(pre, clazz.owner())
+ .subst(sym.typeParams(), args);
+
+ case CompoundType(Type[] parts, _):
+ for (int i = parts.length - 1; i >= 0; i--) {
+ Type result = parts[i].baseType(clazz);
+ if (result != NoType) return result;
+ }
+ break;
+
+ case UnboxedArrayType(_):
+ if (clazz == Global.instance.definitions.ANY_CLASS)
+ return clazz.type();
+ }
+ return NoType;
+ }
+
+ /** Return overriding instance of `sym' in this type,
+ * or `sym' itself if none exists.
+ */
+ public Symbol rebind(Symbol sym) {
+ Symbol sym1 = lookupNonPrivate(sym.name);
+ if (sym1.kind != NONE) {
+ //System.out.println("rebinding " + sym + " to " + sym1);//DEBUG
+ return sym1;
+ }
+ else return sym;
+ }
+
+ /** A map to implement `asSeenFrom'.
+ */
+ static class AsSeenFromMap extends Map {
+
+ private Type pre;
+ private Symbol clazz;
+ private Type illegalType = NoType;
+ private boolean typeArg = false;
+
+ AsSeenFromMap(Type pre, Symbol clazz) {
+ this.pre = pre; this.clazz = clazz;
+ }
+
+ public Type apply(Type t) {
+ switch (t) {
+ case ThisType(Symbol sym):
+ return t.toPrefix(pre, clazz);
+
+ case TypeRef(Type prefix, Symbol sym, Type[] args):
+ if (sym.kind == ALIAS) {
+ return apply(t.unalias());
+ } else if (sym.owner().isPrimaryConstructor()) {
+ assert sym.kind == TYPE;
+ Type t1 = t.toInstance(pre, clazz);
+ //System.out.println(t + ".toInstance(" + pre + "," + clazz + ") = " + t1);//DEBUG
+ switch (t1) {
+ case CovarType(Type tp):
+ if (!covariantOK) {
+ if (illegalType == NoType) illegalType = t1;
+ } else if (!typeArg) {
+ t1 = tp;
+ }
+ }
+ return t1;
+ } else {
+ Type prefix1 = prefix.toPrefix(pre, clazz);
+ Symbol sym1 = (prefix1 == prefix || (sym.flags & MODUL) != 0)
+ ? sym : prefix1.rebind(sym);
+ boolean prevTypeArg = typeArg;
+ typeArg = true;
+ Type[] args1 = map(args);
+ typeArg = prevTypeArg;
+ if (prefix1 == prefix && args1 == args) return t;
+ else return TypeRef(prefix1, sym1, args1);
+ }
+
+ case SingleType(Type prefix, Symbol sym):
+ try {
+ Type prefix1 = prefix.toPrefix(pre, clazz);
+ if (prefix1 == prefix) return t;
+ else return singleType(prefix1, prefix1.rebind(sym));
+ } catch (Type.Error ex) {
+ return apply(t.widen());
+ }
+ break;
+
+ default:
+ return map(t);
+ }
+ }
+
+ void checkLegal(Type tp) {
+ if (illegalType != NoType) {
+ throw new ApplicationError(
+ "malformed type: " + tp + "; " +
+ illegalType + " does not occur in covariant position");
+ }
+ }
+ }
+ //where
+ Type toInstance(Type pre, Symbol clazz) {
+ if (pre == NoType || clazz.kind != CLASS)
+ return this;
+ Symbol sym = symbol();
+ Symbol ownclass = sym.owner().constructorClass();
+ if (ownclass.isSubClass(clazz) &&
+ pre.symbol().isSubClass(ownclass)) {
+ switch (pre.baseType(ownclass)) {
+ case TypeRef(_, Symbol basesym, Type[] baseargs):
+ Symbol[] baseparams = basesym.typeParams();
+ for (int i = 0; i < baseparams.length; i++)
+ if (sym == baseparams[i]) return baseargs[i];//???
+ break;
+ case ErrorType:
+ return ErrorType;
+ }
+ throw new ApplicationError(
+ this + " cannot be instantiated from " + pre);
+ } else {
+ return toInstance(
+ pre.baseType(clazz).prefix(), clazz.owner());
+ }
+ }
+
+ Type toPrefix(Type pre, Symbol clazz) {
+ if (pre == NoType || clazz.kind != CLASS)
+ return this;
+ if (symbol().isSubClass(clazz) &&
+ pre.symbol().isSubClass(symbol())) {
+ if (!pre.isStable()) {
+ throw new Type.Error (
+ "malformed type: " + pre + "." + symbol().nameString());
+ }
+ return pre;
+ } else {
+ return toPrefix(
+ pre.baseType(clazz).prefix(), clazz.owner());
+ }
+ }
+
+ /** This type Types as seen from prefix `pre' and class `clazz'. This means:
+ * Replace all this thistypes of `clazz' or one of its superclasses by `pre'.
+ * Proceed analogously for thistypes referring to outer classes.
+ */
+ public Type asSeenFrom(Type pre, Symbol clazz) {
+ //System.out.println("computing asseenfrom of " + this + " with " + pre + "," + clazz);//DEBUG
+ AsSeenFromMap f = new AsSeenFromMap(pre, clazz);
+ Type t = f.apply(this);
+ f.checkLegal(t);
+ return t;
+ }
+
+ /** Types `these' as seen from prefix `pre' and class `clazz'.
+ */
+ public static Type[] asSeenFrom(Type[] these, Type pre, Symbol clazz) {
+ AsSeenFromMap f = new AsSeenFromMap(pre, clazz);
+ Type[] these1 = f.map(these);
+ for (int i = 0; i < these1.length; i++)
+ f.checkLegal(these1[i]);
+ return these1;
+ }
+
+ /** The info of `sym', seen as a member of this type.
+ */
+ public Type memberInfo(Symbol sym) {
+ return memberTransform(sym, sym.info());
+ }
+
+ /** The type of `sym', seen as a member of this type.
+ */
+ public Type memberType(Symbol sym) {
+ return memberTransform(sym, sym.type());
+ }
+
+ private Type memberTransform(Symbol sym, Type tp) {
+ Type tp1 = tp.asSeenFrom(narrow(), sym.owner());
+ Type tp2 = tp1.asSeenFrom(this, widen().symbol());
+ //todo: sym.owner()?
+ //if (Global.instance.debug) System.out.println(this + "/" + widen() + ".memberType(" + sym + ":" + tp + ") = " + tp1 + "/" + tp2);//DEBUG
+ return tp2;
+ }
+
+// Substitutions ---------------------------------------------------------------
+
+ /** A common map superclass for symbol/symbol and type/symbol substitutions.
+ */
+ static abstract class SubstMap extends Map {
+ private Symbol[] from;
+
+ SubstMap(Symbol[] from) {
+ this.from = from;
+ }
+
+ /** Produce replacement type
+ * @param i The index in `from' of the symbol to be replaced.
+ * @param fromtp The type referring to this symbol.
+ */
+ abstract Type replacement(int i, Type fromtp);
+
+ /** Produce new substitution where some symbols are excluded.
+ * @param newfrom The new array of from symbols (without excluded syms)
+ * @param excluded The array of excluded sysmbols
+ */
+ abstract SubstMap exclude(Symbol[] newfrom, Symbol[] excluded);
+
+ public Type apply(Type t) {
+ switch (t) {
+ case TypeRef(ThisType(_), Symbol sym, Type[] args):
+ for (int i = 0; i < from.length; i++) {
+ if (sym == from[i]) return replacement(i, t);
+ }
+ break;
+ case SingleType(ThisType(_), Symbol sym):
+ for (int i = 0; i < from.length; i++) {
+ if (sym == from[i]) return replacement(i, t);
+ }
+ break;
+ case PolyType(Symbol[] tparams, Type result):
+ Symbol[] from1 = excludeSyms(from, tparams, from);
+ if (from1 != from) {
+ SubstMap f = exclude(from1, tparams);
+ Symbol[] tparams1 = f.map(tparams);
+ Type result1 = f.apply(result);
+ if (tparams1 != tparams)
+ result1 = result1.subst(tparams, tparams1);
+ if (tparams1 == tparams && result1 == result) return t;
+ else return PolyType(tparams1, result1);
+ }
+ }
+ return map(t);
+ }
+ //where
+ private boolean contains1(Symbol[] syms, Symbol sym) {
+ int i = 0;
+ while (i < syms.length && syms[i] != sym) i++;
+ return i < syms.length;
+ }
+
+ private int nCommon(Symbol[] from, Symbol[] tparams) {
+ int cnt = 0;
+ for (int i = 0; i < from.length; i++) {
+ if (contains1(tparams, from[i])) cnt++;
+ }
+ return cnt;
+ }
+
+ private Symbol[] excludeSyms(Symbol[] from, Symbol[] tparams, Symbol[] syms) {
+ int n = nCommon(from, tparams);
+ if (n == 0) {
+ return syms;
+ } else {
+ Symbol[] syms1 = new Symbol[syms.length - n];
+ int j = 0;
+ for (int i = 0; i < from.length; i++) {
+ if (!contains1(tparams, from[i])) syms1[j++] = syms[i];
+ }
+ return syms1;
+ }
+ }
+
+ private Type[] excludeTypes(Symbol[] from, Symbol[] tparams, Type[] types) {
+ int n = nCommon(from, tparams);
+ if (n == 0) {
+ return types;
+ } else {
+ Type[] types1 = new Type[types.length - n];
+ int j = 0;
+ for (int i = 0; i < from.length; i++) {
+ if (!contains1(tparams, from[i])) types1[j++] = types[i];
+ }
+ return types1;
+ }
+ }
+ }
+
+ /** A map for symbol/symbol substitutions
+ */
+ static class SubstSymMap extends SubstMap {
+ Symbol[] to;
+ SubstSymMap(Symbol[] from, Symbol[] to) {
+ super(from);
+ this.to = to;
+ }
+ Type replacement(int i, Type fromtp) {
+ switch (fromtp) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ return TypeRef(pre, to[i], args);
+ case SingleType(Type pre, Symbol sym):
+ return singleType(pre, to[i]);
+ default:
+ throw new ApplicationError();
+ }
+ }
+ SubstMap exclude(Symbol[] newfrom, Symbol[] excluded) {
+ return new SubstSymMap(newfrom, excludeSyms(from, excluded, to));
+ }
+ }
+
+ /** A map for type/symbol substitutions
+ */
+ static class SubstTypeMap extends SubstMap {
+ Type[] to;
+ SubstTypeMap(Symbol[] from, Type[] to) {
+ super(from);
+ this.to = to;
+ }
+ Type replacement(int i, Type fromtp) {
+ return to[i];
+ }
+ SubstMap exclude(Symbol[] newfrom, Symbol[] excluded) {
+ return new SubstTypeMap(newfrom, excludeTypes(from, excluded, to));
+ }
+ }
+
+ /** Substitute symbols `to' for occurrences of symbols `from' in this type.
+ */
+ public Type subst(Symbol[] from, Symbol[] to) {
+ assert from.length == to.length
+ : this + ": " + from.length + " != " + to.length;
+ if (from.length != 0 && from != to)
+ return new SubstSymMap(from, to).apply(this);
+ else return this;
+ }
+
+ /** Substitute symbols `to' for occurrences of symbols `from' in these types.
+ */
+ public static Type[] subst(Type[] these, Symbol[] from, Symbol[] to) {
+ assert from.length == to.length;
+ if (these.length != 0 && from.length != 0 && from != to)
+ return new SubstSymMap(from, to).map(these);
+ else return these;
+ }
+
+ /** Substitute types `to' for occurrences of symbols `from' in this type.
+ */
+ public Type subst(Symbol[] from, Type[] to) {
+ assert from.length == to.length
+ : this + ": " + from.length + " != " + to.length;
+ if (from.length != 0)
+ return new SubstTypeMap(from, to).apply(this);
+ else return this;
+ }
+
+ /** Substitute types `to' for occurrences of symbols `from' in these types.
+ */
+ public static Type[] subst(Type[] these, Symbol[] from, Type[] to) {
+ assert from.length == to.length;
+ if (these.length != 0 && from.length != 0)
+ return new SubstTypeMap(from, to).map(these);
+ else return these;
+ }
+
+ /** A map for substitutions of thistypes.
+ */
+ static class SubstThisMap extends Map {
+ Symbol from;
+ Type to;
+ SubstThisMap(Symbol from, Type to) {
+ this.from = from;
+ this.to = to;
+ }
+ public Type apply(Type t) {
+ switch (t) {
+ case ThisType(Symbol sym):
+ if (sym == from) return to;
+ else return t;
+ default:
+ return map(t);
+ }
+ }
+ }
+
+ public Type substThis(Symbol from, Type to) {
+ return new SubstThisMap(from, to).apply(this);
+ }
+
+ public static Type[] substThis(Type[] these, Symbol from, Type to) {
+ return new SubstThisMap(from, to).map(these);
+ }
+
+ static class ContainsMap extends Map {
+ boolean result = false;
+ Symbol sym;
+ ContainsMap(Symbol sym) {
+ this.sym = sym;
+ }
+ public Type apply(Type t) {
+ switch (t) {
+ case TypeRef(Type pre, Symbol sym1, Type[] args):
+ if (sym == sym1) result = true;
+ else { map(pre); map(args); }
+ break;
+ case SingleType(Type pre, Symbol sym1):
+ map(pre);
+ if (sym == sym1) result = true;
+ break;
+ default:
+ map(t);
+ }
+ return t;
+ }
+ }
+
+ /** Does this type contain symbol `sym'?
+ */
+ public boolean contains(Symbol sym) {
+ ContainsMap f = new ContainsMap(sym);
+ f.apply(this);
+ return f.result;
+ }
+
+ /** Does this type contain any of the symbols `syms'?
+ */
+ public boolean containsSome(Symbol[] syms) {
+ for (int i = 0; i < syms.length; i++)
+ if (contains(syms[i])) return false;
+ return true;
+ }
+
+// Comparisons ------------------------------------------------------------------
+
+ /** Is this type a subtype of that type?
+ */
+ public boolean isSubType(Type that) {
+ if (debugSwitch) {
+ for (int i = 0; i < indent; i++) System.out.print(" ");
+ System.out.println(this + " < " + that + "?");
+ indent++;
+ }
+ boolean result = isSubType0(that);
+ if (debugSwitch) {
+ indent--;
+ for (int i = 0; i < indent; i++) System.out.print(" ");
+ System.out.println(result);
+ }
+ return result;
+ }
+
+ public boolean isSubType0(Type that) {
+ if (this == that) return true;
+
+ switch (this) {
+ case ErrorType:
+ case AnyType:
+ return true;
+ }
+
+ switch (that) {
+ case ErrorType:
+ case AnyType:
+ return true;
+
+ case NoType:
+ return false;
+
+ case ThisType(Symbol sym1):
+ switch (this) {
+ case ThisType(Symbol sym):
+ return sym == sym1;
+ case SingleType(Type pre, Symbol sym):
+ return sym.isModule()
+ && sym.moduleClass() == sym1
+ && pre.isSameAs(sym1.owner().thisType());
+ }
+ break;
+
+ case SingleType(Type pre1, Symbol sym1):
+ switch (this) {
+ case SingleType(Type pre, Symbol sym):
+ return sym == sym1 && pre.isSameAs(pre1);
+ case ThisType(Symbol sym):
+ return sym1.isModule()
+ && sym == sym1.moduleClass()
+ && sym.owner().thisType().isSameAs(pre1);
+ }
+ break;
+
+ case TypeRef(Type pre1, Symbol sym1, Type[] args1):
+ switch (this) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (sym == sym1 && pre.isSameAs(pre1) && isSubArgs(args, args1))
+ return true;
+ break;
+ }
+ if (sym1.kind == CLASS) {
+ Type base = this.baseType(sym1);
+ if (this != base && base.isSubType(that))
+ return true;
+ }
+ break;
+
+ case CompoundType(Type[] parts1, Scope members1):
+ int i = 0;
+ while (i < parts1.length && isSubType(parts1[i])) i++;
+ if (i == parts1.length && specializes(members1))
+ return true;
+ break;
+
+ case MethodType(Symbol[] ps1, Type res1):
+ switch (this) {
+ case MethodType(Symbol[] ps, Type res):
+ if (ps.length != ps1.length) return false;
+ for (int i = 0; i < ps.length; i++) {
+ Symbol p1 = ps1[i];
+ Symbol p = ps[i];
+ if (!p1.type().isSubType(p.type()) ||
+ (p1.flags & Modifiers.DEF) != (p.flags & Modifiers.DEF))
+ return false;
+ }
+ return res.isSubType(res1);
+ }
+ break;
+
+ case PolyType(Symbol[] ps1, Type res1):
+ switch (this) {
+ case PolyType(Symbol[] ps, Type res):
+ if (ps.length != ps1.length) return false;
+ for (int i = 0; i < ps.length; i++)
+ if (!ps1[i].info().subst(ps1, ps).isSubType(ps[i].info()))
+ return false;
+ return res.isSubType(res1.subst(ps1, ps));
+ }
+ break;
+
+ case OverloadedType(Symbol[] alts1, Type[] alttypes1):
+ if (isSubSet(alttypes1, alternatives()))
+ return true;
+ break;
+
+ case UnboxedType(int tag1):
+ switch (this) {
+ case UnboxedType(int tag):
+ return tag <= tag1 && tag1 <= DOUBLE && tag1 != CHAR;
+ }
+ break;
+
+ case UnboxedArrayType(Type elemtp1):
+ switch (this) {
+ case UnboxedArrayType(Type elemtp):
+ return !(elemtp1 instanceof UnboxedType) && elemtp.isSubType(elemtp1);
+ }
+ break;
+
+ case TypeVar(Type origin, Constraint constr):
+ //todo: should we test for equality with origin?
+ if (constr.inst != NoType) {
+ return this.isSubType(constr.inst);
+ } else if (!this.isCovarType()) {
+ constr.lobounds = new List(this, constr.lobounds);
+ return true;
+ }
+ break;
+
+ default:
+ throw new ApplicationError(this + " <: " + that);
+ }
+
+ switch (this) {
+ case NoType:
+ return false;
+ case ThisType(_):
+ case SingleType(_, _):
+ if (this.widen().isSubType(that)) return true;
+ break;
+ case TypeVar(Type origin, Constraint constr):
+ if (constr.inst != NoType) {
+ return constr.inst.isSubType(that);
+ } else {
+ constr.hibounds = new List(that.dropVariance(), constr.hibounds);
+ return true;
+ }
+
+ case TypeRef(_, Symbol sym, _):
+ if (sym.kind == ALIAS) return this.unalias().isSubType(that);
+ break;
+
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ for (int i = 0; i < alttypes.length; i++) {
+ if (alttypes[i].isSameAs0(that)) return true;
+ }
+ break;
+ }
+
+ switch (that) {
+ case TypeRef(_, Symbol sym1, _):
+ if (sym1.kind == ALIAS) return this.isSubType(that.unalias());
+ break;
+ }
+
+ return false;
+ }
+
+ /** Are types `these' subtypes of corresponding types `those'?
+ */
+ public static boolean isSubType(Type[] these, Type[] those) {
+ if (these.length != those.length) return false;
+ for (int i = 0; i < these.length; i++) {
+ if (!these[i].isSubType(those[i])) return false;
+ }
+ return true;
+ }
+
+ /** Are types `these' arguments types conforming to corresponding types `those'?
+ */
+ static boolean isSubArgs(Type[] these, Type[] those) {
+ if (these.length != those.length) return false;
+ for (int i = 0; i < these.length; i++) {
+ switch (those[i]) {
+ case CovarType(Type tp1):
+ switch (these[i]) {
+ case CovarType(Type tp):
+ if (!tp.isSubType(tp1)) return false;
+ break;
+ default:
+ if (!these[i].isSubType(tp1)) return false;
+ }
+ break;
+ default:
+ if (these[i].isCovarType() || !these[i].isSameAs(those[i]))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isSubSet(Type[] alts, Type[] alts1) {
+ for (int i = 0; i < alts.length; i++) {
+ int j = 0;
+ while (j < alts1.length && !alts1[j].isSameAs(alts[i])) j++;
+ if (j == alts1.length) return false;
+ }
+ return true;
+ }
+
+ /** Does this type implement all symbols in scope `s' with same or stronger types?
+ */
+ public boolean specializes(Scope s) {
+ for (Scope.Entry e = s.elems; e != Scope.Entry.NONE; e = e.next)
+ if (!specializes(e.sym)) return false;
+ return true;
+ }
+
+ /** Does this type implement symbol `sym1' with same or stronger type?
+ */
+ public boolean specializes(Symbol sym1) {
+ if (debugSwitch) {
+ for (int i = 0; i < indent; i++) System.out.print(" ");
+ System.out.println(this + " specializes " + sym1 + "?");
+ indent++;
+ }
+ boolean result = specializes0(sym1);
+ if (debugSwitch) {
+ indent--;
+ for (int i = 0; i < indent; i++) System.out.print(" ");
+ System.out.println(result);
+ }
+ return result;
+ }
+
+ public boolean specializes0(Symbol sym1) {
+ Symbol sym = lookup(sym1.name);
+ Type self = narrow();
+ return
+ sym == sym1 ||
+ ((sym.kind == sym1.kind || sym1.kind == TYPE) &&
+ self.memberInfo(sym).isSubType(
+ sym1.info().substThis(sym.owner(), self))) ||
+ (sym.kind == TYPE && sym1.kind == ALIAS &&
+ sym1.info().unalias().isSameAs(sym.type()));
+ }
+
+ /** Is this type the same as that type?
+ */
+ public boolean isSameAs(Type that) {
+ if (debugSwitch) {
+ for (int i = 0; i < indent; i++) System.out.print(" ");
+ System.out.println(this + " = " + that + "?");
+ indent++;
+ }
+ boolean result = isSameAs0(that);
+ if (debugSwitch) {
+ indent--;
+ for (int i = 0; i < indent; i++) System.out.print(" ");
+ System.out.println(result);
+ }
+ return result;
+ }
+
+ public boolean isSameAs0(Type that) {
+ if (this == that) return true;
+
+ switch (this) {
+ case ErrorType:
+ case AnyType:
+ return true;
+
+ case ThisType(Symbol sym):
+ switch (that) {
+ case ThisType(Symbol sym1):
+ return sym == sym1;
+ case SingleType(Type pre1, Symbol sym1):
+ return sym1.isModule()
+ && sym == sym1.moduleClass()
+ && sym.owner().thisType().isSameAs(pre1);
+ }
+ break;
+
+ case SingleType(Type pre, Symbol sym):
+ switch (that) {
+ case SingleType(Type pre1, Symbol sym1):
+ return sym == sym1 && pre.isSameAs(pre1);
+ case ThisType(Symbol sym1):
+ return sym.isModule()
+ && sym.moduleClass() == sym1
+ && pre.isSameAs(sym1.owner().thisType());
+ }
+ break;
+
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ switch (that) {
+ case TypeRef(Type pre1, Symbol sym1, Type[] args1):
+ if (sym == sym1 && pre.isSameAs(pre1) && isSameArgs(args, args1))
+ return true;
+ }
+ break;
+
+ case CompoundType(Type[] parts, Scope members):
+ switch (that) {
+ case CompoundType(Type[] parts1, Scope members1):
+ if (parts.length != parts1.length) return false;
+ for (int i = 0; i < parts.length; i++)
+ if (!parts[i].isSameAs(parts1[i])) return false;
+ return isSameAs(members, members1);
+ }
+ break;
+
+ case MethodType(Symbol[] ps, Type res):
+ switch (that) {
+ case MethodType(Symbol[] ps1, Type res1):
+ if (ps.length != ps1.length) return false;
+ for (int i = 0; i < ps.length; i++) {
+ Symbol p1 = ps1[i];
+ Symbol p = ps[i];
+ if (!p1.type().isSameAs(p.type()) ||
+ (p1.flags & Modifiers.DEF) != (p.flags & Modifiers.DEF))
+ return false;
+ }
+ return res.isSameAs(res1);
+ }
+ break;
+
+ case PolyType(Symbol[] ps, Type res):
+ switch (that) {
+ case PolyType(Symbol[] ps1, Type res1):
+ if (ps.length != ps1.length) return false;
+ for (int i = 0; i < ps.length; i++)
+ if (!ps1[i].info().subst(ps1, ps).isSameAs(ps[i].info()))
+ return false;
+ return res.isSameAs(res1.subst(ps1, ps));
+ }
+ break;
+
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ switch (that) {
+ case OverloadedType(Symbol[] alts1, Type[] alttypes1):
+ return isSubSet(alttypes1, alttypes)
+ && isSubSet(alttypes, alttypes1);
+ }
+ break;
+
+ case UnboxedType(int kind):
+ switch (that) {
+ case UnboxedType(int kind1):
+ return kind == kind1;
+ }
+ break;
+
+ case UnboxedArrayType(Type elemtp):
+ switch (that) {
+ case UnboxedArrayType(Type elemtp1):
+ return elemtp.isSameAs(elemtp1);
+ }
+ break;
+ }
+
+ switch (that) {
+ case ErrorType:
+ case AnyType:
+ return true;
+ case NoType:
+ return false;
+ case TypeVar(Type origin, Constraint constr):
+ if (constr.inst != NoType)
+ return constr.inst.isSameAs(this);
+ else if (!this.isCovarType())
+ return constr.instantiate(this.any2typevar());
+ }
+
+ switch (this) {
+ case NoType:
+ return false;
+ case TypeVar(Type origin, Constraint constr):
+ if (constr.inst != NoType)
+ return constr.inst.isSameAs(that);
+ else if (!that.isCovarType())
+ return constr.instantiate(that.any2typevar());
+ break;
+ case TypeRef(_, Symbol sym, _):
+ if (sym.kind == ALIAS) return this.unalias().isSameAs(that);
+ }
+
+ switch (that) {
+ case TypeRef(_, Symbol sym, _):
+ if (sym.kind == ALIAS) return this.isSameAs(that.unalias());
+ }
+
+ return false;
+ }
+
+ /** Is this type argument that same as `that'?
+ */
+ public boolean isSameArg(Type that) {
+ switch (this) {
+ case CovarType(Type tp):
+ switch (that) {
+ case CovarType(Type tp1):
+ return tp.isSameAs(tp1);
+ }
+ }
+ return this.isSameAs(that);
+ }
+
+ /** Are types `these' the same as corresponding types `those'?
+ */
+ public static boolean isSameAs(Type[] these, Type[] those) {
+ if (these.length != those.length) return false;
+ for (int i = 0; i < these.length; i++) {
+ if (!these[i].isSameAs(those[i])) return false;
+ }
+ return true;
+ }
+
+ /** Are type arguments `these' the same as corresponding types `those'?
+ */
+ public static boolean isSameArgs(Type[] these, Type[] those) {
+ if (these.length != those.length) return false;
+ for (int i = 0; i < these.length; i++) {
+ if (!these[i].isSameArg(those[i])) return false;
+ }
+ return true;
+ }
+
+ /** Do scopes `s1' and `s2' define he same symbols with the same kinds and infos?
+ */
+ public boolean isSameAs(Scope s1, Scope s2) {
+ return isSubScope(s1, s2) && isSubScope(s2, s1);
+ }
+
+ /** Does scope `s1' define all symbols of scope `s2' with the same kinds and infos?
+ */
+ private boolean isSubScope(Scope s1, Scope s2) {
+ for (Scope.Entry e = s2.elems; e != Scope.Entry.NONE; e = e.next) {
+ Symbol sym2 = e.sym;
+ Symbol sym1 = s1.lookup(sym2.name);
+ if (sym1.kind != sym2.kind ||
+ !sym1.info().isSameAs(
+ sym2.info().substThis(sym2.owner(), sym1.owner().thisType())))
+ return false;
+ }
+ return true;
+ }
+
+ boolean isSameAsAll(Type[] tps) {
+ int i = 1;
+ while (i < tps.length && isSameAs(tps[i])) i++;
+ return i == tps.length;
+ }
+
+ /** Map every occurrence of AnyType to a fresh type variable.
+ */
+ public static Map any2typevarMap = new Map() {
+ public Type apply(Type t) { return t.any2typevar(); }
+ };
+
+ public Type any2typevar() {
+ switch (this) {
+ case AnyType:
+ return TypeVar(this, new Constraint());
+ default:
+ return any2typevarMap.map(this);
+ }
+ }
+
+// Closures and Least Upper Bounds ---------------------------------------------------
+
+ /** The closure of this type, i.e. the widened type itself followed by all
+ * its direct and indirect (pre-) base types, sorted by Symbol.isLess().
+ * Note that (pre-) base types do _not_ carry type parameters; these
+ * are added by baseType().
+ */
+ public Type[] closure() {
+ switch (this.widen().unalias()) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ return subst(
+ asSeenFrom(sym.closure(), pre, sym.owner()),
+ sym.typeParams(), args);
+
+ case CompoundType(Type[] parts, Scope members):
+ Type[][] closures = new Type[parts.length][];
+ for (int i = 0; i < parts.length; i++)
+ closures[i] = parts[i].closure();
+ return union(closures);
+
+ default:
+ return new Type[]{this};
+ }
+ }
+
+ /** return union of array of closures
+ */
+ static private Type[] union(Type[][] closures) {
+ if (closures.length == 1) return closures[0]; // fast special case
+ int[] index = new int[closures.length];
+ int totalsize = 0;
+ for (int i = 0; i < index.length; i++) {
+ index[i] = 0;
+ totalsize = totalsize + closures[i].length;
+ }
+ Type[] res = new Type[totalsize];
+ int j = 0;
+
+ while (true) {
+ // find minimal element
+ Type min = null;
+ for (int i = 0; i < index.length; i++) {
+ if (index[i] < closures[i].length) {
+ Type cltype = closures[i][index[i]];
+ if (min == null ||
+ cltype.symbol().isLess(min.symbol()) ||
+ cltype.symbol() == min.symbol() && cltype.isSubType(min)) {
+ min = cltype;
+ }
+ }
+ }
+ if (min == null) break;
+
+ res[j] = min;
+ j = j + 1;
+
+ // bump all indices that start with minimal element
+ for (int i = 0; i < index.length; i++) {
+ if (index[i] < closures[i].length &&
+ closures[i][index[i]].symbol() == min.symbol())
+ index[i] = index[i] + 1;
+ }
+ }
+ Type[] result = new Type[j];
+ System.arraycopy(res, 0, result, 0, j);
+ return result;
+ }
+
+ /** return intersection of array of closures
+ */
+ static private Type[] intersection(Type[][] closures) {
+ if (closures.length == 1) return closures[0]; // fast special case
+ int[] index = new int[closures.length];
+ Type[] mintypes = new Type[closures.length];
+ int minsize = Integer.MAX_VALUE;
+ for (int i = 0; i < index.length; i++) {
+ index[i] = 0;
+ if (closures[i].length < minsize) minsize = closures[i].length;
+ }
+ Type[] res = new Type[minsize];
+ int j = 0;
+
+ L:
+ while (true) {
+ // find minimal element
+ Symbol min = null;
+ for (int i = 0; i < index.length; i++) {
+ if (index[i] == closures[i].length) break L;
+ Symbol clsym = closures[i][index[i]].symbol();
+ if (min == null || clsym.isLess(min)) min = clsym;
+ }
+
+ boolean agree = true;
+ // bump all indices that start with minimal element
+ for (int i = 0; i < index.length; i++) {
+ Type cltype = closures[i][index[i]];
+ if (cltype.symbol() == min) {
+ mintypes[i] = cltype;
+ index[i] = index[i] + 1;
+ } else {
+ agree = false;
+ }
+ }
+ if (agree) {
+ Type mintype;
+ mintype = commonType(mintypes);
+ if (mintype == NoType)
+ mintype = arglub(mintypes);
+ if (mintype.symbol().kind == CLASS) {
+ res[j] = mintype;
+ j = j + 1;
+ }
+ }
+ }
+ Type[] result = new Type[j];
+ System.arraycopy(res, 0, result, 0, j);
+ return result;
+ }
+
+ //todo: catch lubs not within bounds.
+ static Type arglub(Type[] types) {
+ Type pre = types[0].prefix();
+ Symbol sym = types[0].symbol();
+ Type[] args = new Type[sym.typeParams().length];
+ Type[][] argss = new Type[args.length][types.length];
+ for (int i = 0; i < types.length; i++) {
+ switch (types[i]) {
+ case TypeRef(Type pre1, Symbol sym1, Type[] args1):
+ assert sym == sym1;
+ assert args1.length == args.length;
+ if (!pre.isSameAs(pre1)) return NoType;
+ for (int j = 0; j < args1.length; j++)
+ argss[j][i] = args1[j];
+ case ErrorType:
+ return ErrorType;
+ default:
+ assert false : types[i];
+ }
+ }
+ for (int j = 0; j < args.length; j++) {
+ args[j] = commonType(argss[j]);
+ if (args[j] == NoType)
+ args[j] = CovarType(lub(argss[j]));
+ }
+ return TypeRef(pre, sym, args);
+ }
+
+ /** The frontier of a closure C is the minimal set of types such that
+ * the union of the closures of these types equals C.
+ */
+ static private Type[] frontier(Type[] closure) {
+ Type[] front = new Type[closure.length];
+ int j = 0;
+ for (int i = 0; i < closure.length; i++) {
+ int k = 0;
+ Type tp = closure[i];
+ while (k < j && !front[k].symbol().isSubClass(tp.symbol()))
+ k++;
+ if (k == j) {
+ front[j] = tp;
+ j++;
+ }
+ }
+ Type[] result = new Type[j];
+ System.arraycopy(front, 0, result, 0, j);
+ return result;
+ }
+
+ /** Return the least upper bound of non-empty array of types `tps'.
+ * todo: do we need to consider refinements as well?
+ */
+ public static Type lub(Type[] tps) {
+ //System.out.println("lub" + ArrayApply.toString(tps));//DEBUG
+ Type lubType = commonType(tps);
+ if (lubType != NoType) return lubType;
+ Type[][] closures = new Type[tps.length][];
+ for (int i = 0; i < tps.length; i++) {
+ if (!tps[i].isObjectType()) return Type.NoType;//todo: change
+ closures[i] = tps[i].closure();
+ }
+ Type[] allBaseTypes = intersection(closures);
+ Type[] leastBaseTypes = frontier(allBaseTypes);
+ if (leastBaseTypes.length == 0) return Type.NoType;
+ Scope members = new Scope();
+ lubType = compoundType(leastBaseTypes, members);
+ Type lubThisType = lubType.narrow();
+ //System.out.println("lubtype = " + lubType);//DEBUG
+
+ Symbol[] rsyms = new Symbol[tps.length];
+ Type[] rtps = new Type[tps.length];
+ for (int i = 0; i < allBaseTypes.length; i++) {
+ for (Scope.Entry e = allBaseTypes[i].members().elems;
+ e != Scope.Entry.NONE;
+ e = e.next) {
+ Name name = e.sym.name;
+ if ((e.sym.flags & PRIVATE) == 0 && lubType.lookup(name) == e.sym) {
+ Type symType = lubThisType.memberInfo(e.sym);
+ int j = 0;
+ while (j < tps.length) {
+ rsyms[j] = tps[j].lookupNonPrivate(name);
+ if (rsyms[j] == e.sym) break;
+ rtps[j] = tps[j].memberType(rsyms[j])
+ .substThis(tps[j].symbol(), lubThisType);
+ if (rtps[j].isSameAs(symType)) break;
+ j++;
+ }
+ if (j == tps.length) {
+ Symbol lubSym = lub(rsyms, rtps, lubType.symbol());
+ if (lubSym.kind != NONE && !lubSym.info().isSameAs(symType))
+ members.enter(lubSym);
+ }
+ }
+ }
+ }
+ //System.out.print("lub "); System.out.print(ArrayApply.toString(tps)); System.out.println(" = " + lubType);//DEBUG
+ if (leastBaseTypes.length == 1 && members.elems == Scope.Entry.NONE)
+ return leastBaseTypes[0];
+ else return lubType;
+ }
+
+ private static Type commonType(Type[] tps) {
+ Type tp = tps[0];
+ if (tp.isSameAsAll(tps)) return tp;
+ tp = tp.widen();
+ if (tp.isSameAsAll(widen(tps))) return tp;
+ return NoType;
+ }
+
+ private static Symbol lub(Symbol[] syms, Type[] tps, Symbol owner) {
+ //System.out.println("lub" + ArrayApply.toString(syms));//DEBUG
+ int lubKind = syms[0].kind;
+ for (int i = 1; i < syms.length; i++) {
+ Symbol sym = syms[i];
+ if (sym.kind == ERROR) return Symbol.NONE;
+ if (sym.isType() && sym.kind != lubKind) lubKind = TYPE;
+ }
+ if (lubKind == syms[0].kind && tps[0].isSameAsAll(tps)) {
+ return syms[0].cloneSymbol();
+ }
+ Type lubType = lub(tps);
+ if (lubType == Type.NoType) return Symbol.NONE;
+ Symbol lubSym;
+ switch (lubKind) {
+ case VAL:
+ lubSym = new TermSymbol(syms[0].pos, syms[0].name, owner, 0);
+ break;
+ case TYPE: case ALIAS: case CLASS:
+ lubSym = new TypeSymbol(TYPE, syms[0].pos, syms[0].name, owner, 0);
+ break;
+ default:
+ throw new ApplicationError();
+ }
+ lubSym.setInfo(lubType);
+ return lubSym;
+ }
+
+// Erasure --------------------------------------------------------------------------
+
+ public static Map erasureMap = new Map() {
+ public Type apply(Type t) { return t.erasure(); }
+ };
+
+ private static final Type[] unboxedType =
+ new Type[LastUnboxedTag + 1 - FirstUnboxedTag];
+ private static final Type[] unboxedArrayType =
+ new Type[LastUnboxedTag + 1 - FirstUnboxedTag];
+ private static final Name[] unboxedName =
+ new Name[LastUnboxedTag + 1 - FirstUnboxedTag];
+ private static final Name[] boxedName =
+ new Name[LastUnboxedTag + 1 - FirstUnboxedTag];
+ private static final Name[] boxedFullName =
+ new Name[LastUnboxedTag + 1 - FirstUnboxedTag];
+
+ private static void mkStdClassType(int kind, String unboxedstr, String boxedstr) {
+ unboxedType[kind - FirstUnboxedTag] = UnboxedType(kind);
+ unboxedArrayType[kind - FirstUnboxedTag] = UnboxedArrayType(unboxedType(kind));
+ unboxedName[kind - FirstUnboxedTag] = Name.fromString(unboxedstr);
+ boxedName[kind - FirstUnboxedTag] = Name.fromString(boxedstr);
+ boxedFullName[kind - FirstUnboxedTag] = Name.fromString("scala." + boxedstr);
+ }
+
+ static {
+ mkStdClassType(BYTE, "byte", "Byte");
+ mkStdClassType(SHORT, "short", "Short");
+ mkStdClassType(CHAR, "char", "Char");
+ mkStdClassType(INT, "int", "Int");
+ mkStdClassType(LONG, "long", "Long");
+ mkStdClassType(FLOAT, "float", "Float");
+ mkStdClassType(DOUBLE, "double", "Double");
+ mkStdClassType(BOOLEAN, "boolean", "Boolean");
+ mkStdClassType(UNIT, "void", "Unit");
+ }
+
+ /** Return unboxed type of given kind.
+ */
+ public static Type unboxedType(int kind) {
+ return unboxedType[kind - FirstUnboxedTag];
+ }
+
+ /** Return unboxed array type of given element kind.
+ */
+ public static Type unboxedArrayType(int kind) {
+ return unboxedArrayType[kind - FirstUnboxedTag];
+ }
+
+ /** Return the name of unboxed type of given kind.
+ */
+ public static Name unboxedName(int kind) {
+ return unboxedName[kind - FirstUnboxedTag];
+ }
+
+ /** Return the name of boxed type of given kind.
+ */
+ public static Name boxedName(int kind) {
+ return boxedName[kind - FirstUnboxedTag];
+ }
+
+ /** Return the full name of boxed type of given kind.
+ */
+ public static Name boxedFullName(int kind) {
+ return boxedFullName[kind - FirstUnboxedTag];
+ }
+
+ /** If type is boxed, return its unboxed equivalent; otherwise return the type
+ * itself.
+ */
+ public Type unbox() {
+ switch (this) {
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if ((sym.flags & MODUL) == 0) {
+ Name fullname = sym.fullName();
+ if (fullname == Names.scala_Array && args.length == 1
+ /*&& args[0].unalias().symbol().kind != TYPE Q: why needed?*/) {
+ return UnboxedArrayType(args[0].erasure());
+ } else {
+ for (int i = 0; i < boxedFullName.length; i++) {
+ if (boxedFullName[i] == fullname) return unboxedType[i];
+ }
+ }
+ }
+ }
+ return this;
+ }
+
+ /** Return the erasure of this type.
+ */
+ public Type erasure() {
+ switch (this) {
+ case ThisType(_):
+ case SingleType(_, _):
+ return widen().erasure();
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ switch (sym.kind) {
+ case ALIAS: case TYPE:
+ return pre.memberInfo(sym).erasure();
+
+ case CLASS:
+ if (sym.fullName() == Names.java_lang_Object ||
+ sym.fullName() == Names.scala_AnyRef ||
+ sym.fullName() == Names.scala_AnyVal)
+ return Global.instance.definitions.ANY_TYPE;
+ else {
+ Type this1 = unbox();
+ if (this1 != this) return this1;
+ else if (args.length == 0) return this;
+ else return TypeRef(pre, sym, Type.EMPTY_ARRAY);
+ }
+
+ default: throw new ApplicationError();
+ }
+ case CompoundType(Type[] parents, _):
+ if (parents.length > 0) return parents[0].erasure();
+ else return this;
+ case CovarType(Type tp):
+ return tp.erasure(); // note: needed because of UnboxedArrayType
+ case MethodType(Symbol[] params, Type tp):
+ Symbol[] params1 = erasureMap.map(params);
+ Type tp1 = tp.erasure();
+ switch (tp1) {
+ case MethodType(Symbol[] params2, Type tp2):
+ Symbol[] newparams = new Symbol[params1.length + params2.length];
+ System.arraycopy(params1, 0, newparams, 0, params1.length);
+ System.arraycopy(params2, 0, newparams, params1.length, params2.length);
+ return MethodType(newparams, tp2);
+ default:
+ if (params1 == params && tp1 == tp) return this;
+ else return MethodType(params1, tp1);
+ }
+ case PolyType(_, Type result):
+ return result.erasure();
+ default:
+ return erasureMap.map(this);
+ }
+ }
+
+// Object Interface -----------------------------------------------------------------
+
+ public String toString() {
+ switch (this) {
+ case ErrorType:
+ return "<error>";
+ case AnyType:
+ return "<any type>";
+ case NoType:
+ return "<notype>";
+ case ThisType(Symbol sym):
+ if (sym.isRoot()) return "<root>.this.type";
+ else if (this == localThisType) return "<local>.this.type";
+ else {
+ Type this1 = (Global.instance.debug) ? this : expandModuleThis();
+ if (this1 == this) return sym.nameString() + ".this.type";
+ else return this1.toString();
+ }
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ if (sym.isRoot()) return "<root>";
+ if (!Global.instance.debug) {
+ if (isTupleType())
+ return ArrayApply.toString(
+ dropVarianceMap.map(args), "[", ",", "]");
+ if (isFunctionType()) {
+ Type[] params = new Type[args.length - 1];
+ System.arraycopy(args, 0, params, 0, params.length);
+ return ArrayApply.toString(params, "(", ",", ") => ") +
+ args[params.length].dropVariance();
+ }
+ }
+ Type pre1 = (Global.instance.debug) ? pre : pre.expandModuleThis();
+ String result = pre1.prefixString() + sym.nameString() + sym.idString();
+ if (args.length != 0)
+ result = result + ArrayApply.toString(args, "[", ",", "]");
+ return result;
+ case SingleType(Type pre, Symbol sym):
+ if ((sym.flags & SYNTHETIC) != 0 && !Global.instance.debug)
+ return widen().toString();
+ if (sym.isRoot()) return "<root>.type";
+ Type pre1 = (Global.instance.debug) ? pre : pre.expandModuleThis();
+ return pre1.prefixString() + sym.nameString() + sym.idString() + ".type";
+ case CompoundType(Type[] parts, Scope members):
+ validate();//debug
+ StringBuffer buf = new StringBuffer();
+ if (parts.length > 0) {
+ buf.append(parts[0].toString());
+ int i = 1;
+ while (i < parts.length) {
+ buf.append(" with ");
+ buf.append(parts[i].toString());
+ i++;
+ }
+ }
+ boolean first = true;
+ for (Scope.SymbolIterator it = members.iterator(); it.hasNext(); ) {
+ Symbol sym = it.next();
+ buf.append(first ? "{" : ", ");
+ first = false;
+ buf.append(sym.defString());
+ }
+ if (!first) buf.append("}");
+ return buf.toString();
+ case MethodType(Symbol[] vparams, Type result):
+ return ArrayApply.toString(Symbol.type(vparams), "(", ",", ")") + result;
+ case PolyType(Symbol[] tparams, Type result):
+ return ArrayApply.toString(Symbol.defString(tparams), "[", ",", "]") +
+ result;
+ case CovarType(Type tp):
+ return "+" + tp;
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ return "<overloaded> " + ArrayApply.toString(alttypes, "", " <and> ", "");//debug
+ case TypeVar(Type origin, Constraint constr):
+ if (constr.inst != NoType) return constr.inst.toString();
+ else return origin + "?";
+ case UnboxedType(int kind):
+ return unboxedName(kind).toString();
+ case UnboxedArrayType(Type elemtp):
+ return elemtp.toString() + "[]";
+ case LazyType():
+ return "<lazy type " + getClass() + ">";
+ default:
+ return "<unknown type " + getClass() + ">";
+ }
+ }
+
+ public String toLongString() {
+ String str = toString();
+ if (str.endsWith(".type")) return str + " (with underlying type " + widen() + ")";
+ else return str;
+ }
+
+ private String prefixString() {
+ if ((this == localThisType || symbol().isRoot()) && !Global.instance.debug) {
+ return "";
+ } else {
+ String spre = toString();
+ if (spre.length() == 0)
+ return "";
+ else if (spre.endsWith(".type"))
+ return spre.substring(0, spre.length() - 4);
+ else
+ return spre + "@";
+ }
+ }
+
+ public int hashCode() {
+ switch (this) {
+ case ErrorType:
+ return ERROR;
+ case NoType:
+ return NOtpe;
+ case ThisType(Symbol sym):
+ return THIStpe
+ ^ (sym.hashCode() * 41);
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ return NAMEDtpe
+ ^ (pre.hashCode() * 41)
+ ^ (sym.hashCode() * (41*41))
+ ^ (hashCode(args) * (41*41*41));
+ case SingleType(Type pre, Symbol sym):
+ return SINGLEtpe
+ ^ (pre.hashCode() * 41)
+ ^ (sym.hashCode() * (41*41));
+ case CompoundType(Type[] parts, Scope members):
+ return COMPOUNDtpe
+ ^ (hashCode(parts) * 41)
+ ^ (members.hashCode() * (41 * 41));
+ case MethodType(Symbol[] vparams, Type result):
+ return METHODtpe
+ ^ (hashCode(Symbol.type(vparams)) * 41)
+ ^ (result.hashCode() * (41 * 41));
+ case PolyType(Symbol[] tparams, Type result):
+ return POLYtpe
+ ^ (hashCode(tparams) * 41)
+ ^ (result.hashCode() * (41 * 41));
+ case CovarType(Type tp):
+ return COVARtpe
+ ^ (tp.hashCode() * (41));
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ return OVERLOADEDtpe
+ ^ (hashCode(alts) * 41)
+ ^ (hashCode(alttypes) * (41 * 41));
+ case UnboxedType(int kind):
+ return UNBOXEDtpe
+ ^ (kind * 41);
+ case UnboxedArrayType(Type elemtp):
+ return UNBOXEDARRAYtpe
+ ^ (elemtp.hashCode() * 41);
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ public static int hashCode(Object[] elems) {
+ int h = 0;
+ for (int i = 0; i < elems.length; i++)
+ h = h * 41 + elems[i].hashCode();
+ return h;
+ }
+
+ public static int hashCode(Scope.Entry elems) {
+ int h = 0;
+ for (Scope.Entry e = elems; e != Scope.Entry.NONE; e = e.next)
+ h = h * 41
+ + e.sym.kind
+ + e.sym.name.hashCode()
+ + e.sym.info().hashCode();
+ return h;
+ }
+
+ // todo: change in relation to needs.
+
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof Type) {
+ Type that = (Type) other;
+ switch (this) {
+ case ErrorType:
+ return that == ErrorType;
+ case NoType:
+ return that == NoType;
+ case ThisType(Symbol sym):
+ switch (that) {
+ case ThisType(Symbol sym1):
+ return sym == sym1;
+ default: return false;
+ }
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ switch (that) {
+ case TypeRef(Type pre1, Symbol sym1, Type[] args1):
+ return pre.equals(pre1) && sym == sym1 && equals(args, args1);
+ default: return false;
+ }
+ case SingleType(Type pre, Symbol sym):
+ switch (that) {
+ case SingleType(Type pre1, Symbol sym1):
+ return pre.equals(pre1) && sym == sym1;
+ default: return false;
+ }
+ case CompoundType(Type[] parts, Scope members):
+ switch (that) {
+ case CompoundType(Type[] parts1, Scope members1):
+ return parts.equals(parts1) && members.equals(members1);
+ default: return false;
+ }
+ case MethodType(Symbol[] vparams, Type result):
+ switch (that) {
+ case MethodType(Symbol[] vparams1, Type result1):
+ return equals(Symbol.type(vparams), Symbol.type(vparams1)) &&
+ result.equals(result1);
+ default: return false;
+ }
+ case PolyType(Symbol[] tparams, Type result):
+ switch (that) {
+ case PolyType(Symbol[] tparams1, Type result1):
+ return equals(tparams, tparams1) && result.equals(result1);
+ default: return false;
+ }
+ case CovarType(Type tp):
+ switch (that) {
+ case CovarType(Type tp1):
+ return tp.equals(tp1);
+ default: return false;
+ }
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ switch (that) {
+ case OverloadedType(Symbol[] alts1, Type[] alttypes1):
+ return equals(alts, alts1) && equals(alttypes, alttypes1);
+ default: return false;
+ }
+ case UnboxedType(int kind):
+ switch (that) {
+ case UnboxedType(int kind1):
+ return kind == kind1;
+ default: return false;
+ }
+ case UnboxedArrayType(Type elemtp):
+ switch (that) {
+ case UnboxedArrayType(Type elemtp1):
+ return elemtp.equals(elemtp1);
+ default: return false;
+ }
+ default:
+ }
+ }
+ return false;
+ }
+
+ public static boolean equals(Object[] elems1, Object[] elems2) {
+ if (elems1.length != elems2.length) return false;
+ for (int i = 0; i < elems1.length; i++) {
+ if (!elems1[i].equals(elems2[i])) return false;
+ }
+ return true;
+ }
+
+// Type.List class -----------------------------------------------------------------
+
+ /** A class for lists of types.
+ */
+ public static class List {
+ public Type head;
+ public List tail;
+ public List(Type head, List tail) {
+ this.head = head; this.tail = tail;
+ }
+ public int length() {
+ return (this == EMPTY) ? 0 : 1 + tail.length();
+ }
+ public Type[] toArray() {
+ Type[] ts = new Type[length()];
+ copyToArray(ts, 0, 1);
+ return ts;
+ }
+ public void copyToArray(Type[] ts, int start, int delta) {
+ if (this != EMPTY) {
+ ts[start] = head;
+ tail.copyToArray(ts, start+delta, delta);
+ }
+ }
+ public Type[] toArrayReverse() {
+ Type[] ts = new Type[length()];
+ copyToArray(ts, ts.length - 1, -1);
+ return ts;
+ }
+
+ public static List EMPTY = new List(null, null);
+
+ public static List append(List l, Type tp) {
+ return (l == EMPTY) ? new List(tp, EMPTY)
+ : new List(l.head, append(l.tail, tp));
+ }
+ }
+
+// Type.Constraint class -------------------------------------------------------
+
+ /** A class for keeping sub/supertype constraints and instantiations
+ * of type variables.
+ */
+ public static class Constraint {
+ public List lobounds = List.EMPTY;
+ public List hibounds = List.EMPTY;
+ public Type inst = NoType;
+
+ public boolean instantiate(Type tp) {
+ for (List l = lobounds; l != List.EMPTY; l = l.tail) {
+ if (!l.head.isSubType(tp)) return false;
+ }
+ for (List l = hibounds; l != List.EMPTY; l = l.tail) {
+ if (!tp.isSubType(l.head)) return false;
+ }
+ inst = tp;
+ return true;
+ }
+ }
+
+// Type.Error class --------------------------------------------------------------
+
+ /** A class for throwing type errors
+ */
+ public static class Error extends java.lang.Error {
+ public String msg;
+ public Error(String msg) {
+ super(msg);
+ this.msg = msg;
+ }
+ }
+
+ /** A class for throwing type errors
+ */
+ public static class VarianceError extends Error {
+ public VarianceError(String msg) {
+ super(msg);
+ }
+ }
+}
+
+/* A standard pattern match:
+
+ case ErrorType:
+ case AnyType:
+ case NoType:
+ case ThisType(Symbol sym):
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ case SingleType(Type pre, Symbol sym):
+ case CompoundType(Type[] parts, Scope members):
+ case MethodType(Symbol[] vparams, Type result):
+ case PolyType(Symbol[] tparams, Type result):
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ case CovarType(Type tp):
+*/
+
diff --git a/sources/scalac/symtab/TypeTags.java b/sources/scalac/symtab/TypeTags.java
new file mode 100644
index 0000000000..a3bd103432
--- /dev/null
+++ b/sources/scalac/symtab/TypeTags.java
@@ -0,0 +1,47 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab;
+
+public interface TypeTags {
+
+ /** unboxed type tags
+ */
+ int BYTE = 10;
+ int CHAR = 11;
+ int SHORT = 12;
+ int INT = 13;
+ int LONG = 14;
+ int FLOAT = 15;
+ int DOUBLE = 16;
+ int BOOLEAN = 17;
+ int UNIT = 18;
+ int STRING = 19;
+
+ int FirstUnboxedTag = BYTE;
+ int LastUnboxedTag = UNIT;
+
+ /** other type tags (used for hashcodes and Pickling)
+ */
+ int ERRORtpe = 20;
+ int NOtpe = 21;
+ int THIStpe = 22;
+ int NAMEDtpe = 23;
+ int SINGLEtpe = 24;
+ int COMPOUNDtpe = 25;
+ int METHODtpe = 26;
+ int POLYtpe = 27;
+ int CONSTRUCTORtpe = 28;
+ int COVARtpe = 29;
+ int OVERLOADEDtpe = 30;
+ int UNBOXEDtpe = 31;
+ int UNBOXEDARRAYtpe = 32;
+
+ int firstTypeTag = ERRORtpe;
+ int lastTypeTag = UNBOXEDARRAYtpe;
+}
diff --git a/sources/scalac/symtab/classfile/AttributeParser.java b/sources/scalac/symtab/classfile/AttributeParser.java
new file mode 100644
index 0000000000..0ed09b44d0
--- /dev/null
+++ b/sources/scalac/symtab/classfile/AttributeParser.java
@@ -0,0 +1,439 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.*;
+import scalac.symtab.*;
+import scalac.util.*;
+import java.util.*;
+
+public class AttributeParser implements ClassfileConstants {
+
+ /** the classfile input buffer
+ */
+ protected AbstractFileReader in;
+
+ /** the constant pool
+ */
+ protected ConstantPool pool;
+
+ protected ClassfileParser parser;
+
+ /** constructor
+ */
+ public AttributeParser(AbstractFileReader in, ConstantPool pool, ClassfileParser parser) {
+ this.in = in;
+ this.pool = pool;
+ this.parser = parser;
+ }
+
+ public int nameToId(Name name) {
+ if (name == SOURCEFILE_N)
+ return SOURCEFILE_ATTR;
+ if (name == SYNTHETIC_N)
+ return SYNTHETIC_ATTR;
+ if (name == DEPRECATED_N)
+ return DEPRECATED_ATTR;
+ if (name == CODE_N)
+ return CODE_ATTR;
+ if (name == EXCEPTIONS_N)
+ return EXCEPTIONS_ATTR;
+ if (name == CONSTANT_VALUE_N)
+ return CONSTANT_VALUE_ATTR;
+ if (name == LINE_NUM_TABLE_N)
+ return LINE_NUM_TABLE_ATTR;
+ if (name == LOCAL_VAR_TABLE_N)
+ return LOCAL_VAR_TABLE_ATTR;
+ if (name == INNERCLASSES_N)
+ return INNERCLASSES_ATTR;
+ if (name == META_N)
+ return META_ATTR;
+ if (name == SCALA_N)
+ return SCALA_ATTR;
+ return BAD_ATTR;
+ }
+
+ public Symbol readAttributes(Symbol def, Type type, int attrs) {
+ char nattr = in.nextChar();
+ for (int i = 0; i < nattr; i++) {
+ Name attrName = (Name)pool.readPool(in.nextChar());
+ int attr = nameToId(attrName);
+ int attrLen = in.nextInt();
+ if ((attrs & attr) == 0) {
+ //System.out.println("# skipping " + attrName + " of " + def);
+ in.skip(attrLen);
+ } else {
+ //System.out.println("# reading " + attrName + " of " + def);
+ readAttribute(def, type, attr, attrLen);
+ }
+ }
+ return def;
+ }
+
+ public void readAttribute(Symbol sym, Type type, int attr, int attrLen) {
+ switch (attr) {
+ // class attributes
+ case SCALA_ATTR:
+ in.skip(attrLen);
+ /* not yet
+ Name sourcefile = (Name)pool.readPool(in.nextChar());
+ new UnPickle(
+ (JavaClassSymbol) sym, in.nextBytes(attrLen - 2), sourcefile);
+ */
+ return;
+
+ case SOURCEFILE_ATTR:
+ // ((ClassDef)def).sourcefile = (Name)reader.readPool(in.nextChar());
+ in.skip(attrLen);
+ return;
+
+ case INNERCLASSES_ATTR:
+ /* int n = in.nextChar();
+ for (int i = 0; i < n; i++) {
+ Symbol inner = (Symbol)pool.readPool(in.nextChar());
+ Symbol outer = (Symbol)pool.readPool(in.nextChar());
+ Name name = (Name)pool.readPool(in.nextChar());
+ int flags = in.nextChar();
+ if (name != null) {
+ inner.owner(outer);
+ inner.mangled(name);
+ inner.flags = flags;
+ }
+ } */
+ in.skip(attrLen);
+ return;
+
+ // method attributes
+ case CODE_ATTR:
+ in.skip(attrLen);
+ return;
+
+ case EXCEPTIONS_ATTR:
+ //int nexceptions = in.nextChar();
+ //Type[] thrown = new Type[nexceptions];
+ //for (int j = 0; j < nexceptions; j++)
+ // thrown[j] = make.classType(reader.readClassName(in.nextChar()));
+ //((MethodType)def.type).thrown = thrown;
+ in.skip(attrLen);
+ return;
+
+ case LINE_NUM_TABLE_ATTR:
+ in.skip(attrLen);
+ return;
+
+ case LOCAL_VAR_TABLE_ATTR:
+ in.skip(attrLen);
+ return;
+
+ // general attributes
+ case SYNTHETIC_ATTR:
+ sym.flags |= Modifiers.SYNTHETIC;
+ return;
+
+ case DEPRECATED_ATTR:
+ sym.flags |= Modifiers.DEPRECATED;
+ return;
+
+ case CONSTANT_VALUE_ATTR:
+ // Type ctype = (Type)reader.readPool(in.nextChar());
+ // def.type = types.coerce(ctype, def.type);
+ in.skip(attrLen);
+ return;
+
+ case META_ATTR:
+ //System.out.println("parsing meta data for " + sym);
+ String meta = pool.readPool(in.nextChar()).toString().trim();
+ sym.setInfo(new MetaParser(meta, tvars, sym, type).parse(), parser.phaseId);
+
+ return;
+ }
+ throw new RuntimeException("unknown classfile attribute");
+ }
+
+ Scope tvars = new Scope();
+
+ class MetaParser {
+
+ Symbol owner;
+ StringTokenizer scanner;
+ Type defaultType;
+ String token;
+ Scope tvars;
+ Scope locals;
+
+ MetaParser(String meta, Scope tvars, Symbol owner, Type defaultType) {
+ //System.out.println("meta = " + meta);
+ this.scanner = new StringTokenizer(meta, "()[], \t<;", true);
+ this.defaultType = defaultType;
+ this.owner = owner;
+ this.tvars = tvars;
+ }
+
+ private Symbol getTVar(String name) {
+ return getTVar(name, parser.c.constructor());
+ }
+
+ private Symbol getTVar(String name, Symbol owner) {
+ if (name.startsWith("?")) {
+ if (locals != null) {
+ Symbol s = locals.lookup(Name.fromString(name).toTypeName());
+ if (s != Symbol.NONE)
+ return s;
+ }
+ Symbol s = tvars.lookup(Name.fromString(name).toTypeName());
+ if (s == Symbol.NONE) {
+ s = new TypeSymbol(Kinds.TYPE,
+ Position.NOPOS,
+ Name.fromString(token).toTypeName(),
+ owner,
+ Modifiers.PARAM);
+ s.setInfo(parser.defs.ANY_TYPE, parser.phaseId);
+ tvars.enter(s);
+ }
+ return s;
+ } else
+ return Symbol.NONE;
+ }
+
+ private String nextToken() {
+ do {
+ token = scanner.nextToken().trim();
+ } while (token.length() == 0);
+ return token;
+ }
+
+ protected Type parse() {
+ if (scanner.hasMoreTokens()) {
+ nextToken();
+ if (!scanner.hasMoreTokens())
+ return defaultType;
+ if ("class".equals(token))
+ return parseMetaClass();
+ if ("method".equals(token))
+ return parseMetaMethod();
+ if ("field".equals(token))
+ return parseMetaField();
+ if ("constr".equals(token))
+ return parseConstrField();
+ }
+ return defaultType;
+ }
+
+ protected Type parseMetaClass() {
+ nextToken();
+ //System.out.println("parse meta class " + token);//DEBUG
+ if ("[".equals(token)) {
+ try {
+ Vector syms = new Vector();
+ do {
+ nextToken();
+ assert token.startsWith("?");
+ Symbol s = getTVar(token);
+ if (s == Symbol.NONE)
+ return defaultType;
+ nextToken();
+ //System.out.println("new var " + s + ", " + token);//DEBUG
+ if (token.equals("<")) {
+ nextToken();
+ s.setInfo(parseType(), parser.phaseId);
+ }
+ syms.add(s);
+ } while (token.equals(","));
+ assert "]".equals(token);
+ nextToken();
+ Symbol[] smbls = (Symbol[])syms.toArray(new Symbol[syms.size()]);
+ //System.out.println("*** " + syms);//DEBUG
+ Type constrtype = Type.appliedType(
+ parser.ctype, Symbol.type(smbls));
+
+ if ((parser.c.flags & Modifiers.INTERFACE) != 0) {
+ parser.c.constructor().setInfo(
+ Type.PolyType(smbls, constrtype), parser.phaseId);
+ //System.out.println("info = " + parser.c.constructor().info());//DEBUG
+ }
+ Symbol[] constrs;
+ switch (parser.c.constructor().rawInfo()) {
+ case OverloadedType(Symbol[] alts, _):
+ constrs = alts;
+ break;
+ default:
+ constrs = new Symbol[]{parser.c.constructor()};
+ }
+ for (int i = 0; i < constrs.length; i++) {
+ //System.out.println("info = " + e.sym.info());
+ switch (constrs[i].rawInfo()) {
+ case MethodType(Symbol[] vparams, _):
+ constrs[i].setInfo(
+ Type.PolyType(smbls,
+ Type.MethodType(
+ vparams, constrtype)), parser.phaseId);
+ break;
+ case PolyType(_, _):
+ constrs[i].setInfo(
+ Type.PolyType(smbls, constrtype), parser.phaseId);
+ break;
+ }
+ //System.out.println("*** constructor " + e.sym + ": " + e.sym.info());//DEBUG
+ }
+ } catch (NoSuchElementException e) {
+ }
+ }
+ Type res = defaultType;
+ if ("extends".equals(token)) {
+ Vector basetpes = new Vector();
+ do {
+ nextToken();
+ basetpes.add(parseType());
+ } while (token.equals("with"));
+ switch (defaultType) {
+ case CompoundType(_, Scope scope):
+ res = Type.compoundType(
+ (Type[])basetpes.toArray(new Type[basetpes.size()]),
+ scope,
+ defaultType.symbol());
+ }
+ }
+ assert ";".equals(token);
+ return res;
+ }
+
+ protected Type parseType() {
+ String name = token;
+ Symbol s = getTVar(name);
+ nextToken();
+ if (s != Symbol.NONE)
+ return s.type();
+ Symbol clazz = parser.defs.getClass(Name.fromString(name));
+ if (token.equals("[")) {
+ Vector types = new Vector();
+ do {
+ nextToken();
+ types.add(parseType());
+ } while (token.equals(","));
+ assert "]".equals(token);
+ nextToken();
+ Type[] args = new Type[types.size()];
+ types.toArray(args);
+ return Type.TypeRef(clazz.owner().thisType(), clazz, args);
+ } else {
+ return parser.defs.monoType(clazz);
+ }
+ }
+
+ protected Type parseMetaMethod() {
+ locals = new Scope();
+ try {
+ nextToken();
+ Symbol[] smbls = null;
+ //System.out.println("parse meta method " + token);
+ if ("[".equals(token)) {
+ Vector syms = new Vector();
+ do {
+ nextToken();
+ if ("]".equals(token))
+ break;
+ assert token.startsWith("?");
+ Symbol s = getTVar(token, owner);
+ if (s == Symbol.NONE)
+ return defaultType;
+ nextToken();
+ //System.out.println("new var " + s + ", " + token);
+ if (token.equals("<")) {
+ nextToken();
+ s.setInfo(parseType(), parser.phaseId);
+ }
+ syms.add(s);
+ } while (token.equals(","));
+ assert "]".equals(token);
+ nextToken();
+ smbls = (Symbol[])syms.toArray(new Symbol[syms.size()]);
+ }
+ if ("(".equals(token)) {
+ int i = 0;
+ Vector params = new Vector();
+ do {
+ nextToken();
+ if (")".equals(token))
+ break;
+ params.add(new TermSymbol(
+ Position.NOPOS,
+ Name.fromString("x" + (i++)),
+ owner,
+ Modifiers.PARAM).setInfo(parseType(), parser.phaseId));
+ //System.out.println(" + " + token);
+ } while (token.equals(","));
+ assert ")".equals(token);
+ nextToken();
+ //System.out.println("+++ method " + token);
+ Type restpe = parseType();
+ assert ";".equals(token);
+ if (smbls == null)
+ return Type.MethodType(
+ (Symbol[])params.toArray(new Symbol[params.size()]),
+ restpe);
+ else
+ return Type.PolyType(
+ smbls,
+ Type.MethodType(
+ (Symbol[])params.toArray(new Symbol[params.size()]),
+ restpe));
+ } else {
+ Type res = parseType();
+ assert ";".equals(token);
+ if (smbls == null)
+ return Type.PolyType(Symbol.EMPTY_ARRAY, res);
+ else
+ return Type.PolyType(smbls, res);
+ }
+ } catch (NoSuchElementException e) {
+ return defaultType;
+ } finally {
+ locals = null;
+ }
+ }
+
+ protected Type parseMetaField() {
+ nextToken();
+ return parseType();
+ }
+
+ protected Type parseConstrField() {
+ try {
+ nextToken();
+ //System.out.println("+++ constr " + token);
+ if ("(".equals(token)) {
+ int i = 0;
+ Vector params = new Vector();
+ do {
+ nextToken();
+ if (")".equals(token))
+ break;
+ params.add(new TermSymbol(
+ Position.NOPOS,
+ Name.fromString("x" + (i++)),
+ owner,
+ Modifiers.PARAM).setInfo(parseType(), parser.phaseId));
+ //System.out.println(" + " + token);
+ } while (token.equals(","));
+ assert ")".equals(token);
+ nextToken();
+ assert ";".equals(token);
+ return Type.MethodType(
+ (Symbol[])params.toArray(new Symbol[params.size()]),
+ parser.ctype);
+ } else {
+ assert ";".equals(token);
+ return Type.PolyType(Symbol.EMPTY_ARRAY, parser.ctype);
+ }
+ } catch (NoSuchElementException e) {
+ return defaultType;
+ }
+ }
+ }
+}
diff --git a/sources/scalac/symtab/classfile/ClassParser.java b/sources/scalac/symtab/classfile/ClassParser.java
new file mode 100644
index 0000000000..9948ba33cf
--- /dev/null
+++ b/sources/scalac/symtab/classfile/ClassParser.java
@@ -0,0 +1,111 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.*;
+import scalac.symtab.*;
+import scalac.util.*;
+import java.io.*;
+
+
+public class ClassParser extends Type.LazyType {
+
+ /** the global compilation environment
+ */
+ protected Global global;
+
+ public ClassParser(Global global) {
+ this.global = global;
+ }
+
+ /** complete class symbol c by loading the class
+ */
+ public void complete(Symbol c) {
+ //System.out.println("loading " + c);//DEBUG
+ try {
+ long msec = System.currentTimeMillis();
+ String filename = externalizeFileName(c.fullName()) + ".class";
+ AbstractFile f = global.classPath.openFile(filename);
+ if (f == null)
+ global.error("could not read class " + c);
+ else {
+ new ClassfileParser(global, new AbstractFileReader(f), c).parse();
+ global.operation("loaded " + f.getPath() + " in " +
+ (System.currentTimeMillis() - msec) + "ms");
+ //for (Definition e = c.locals().elems; e != null; e = e.sibling)
+ // if (e.def.kind == TYP)
+ // e.def.complete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ global.error("i/o error while loading " + c);
+ c.setInfo(Type.ErrorType);
+ }
+ }
+
+ /** return external representation of file name s,
+ * converting '.' to File.separatorChar
+ */
+ public String externalizeFileName(Name n) {
+ if ((n == null) || (n.length() == 0))
+ return ".";
+ byte[] ascii = n.toAscii();
+ String s = SourceRepresentation.ascii2string(
+ ascii, 0, ascii.length);
+ return s.replace('.', File.separatorChar);
+ }
+
+ public Type.LazyType staticsParser(Symbol clazz) {
+ return new StaticsParser(clazz);
+ }
+
+ public Type.LazyType aliasParser(Symbol alias) {
+ return new AliasParser(alias);
+ }
+
+ class StaticsParser extends Type.LazyType {
+ Symbol clazz;
+
+ StaticsParser(Symbol clazz) {
+ this.clazz = clazz;
+ }
+
+ public void complete(Symbol statics) {
+ ClassParser.this.complete(clazz);
+ }
+ }
+
+ class AliasParser extends Type.LazyType {
+ Symbol alias;
+
+ AliasParser(Symbol alias) {
+ this.alias = alias;
+ }
+
+ public void complete(Symbol c) {
+ try {
+ long msec = System.currentTimeMillis();
+ String filename = externalizeFileName(alias.fullName()) + ".class";
+ AbstractFile f = global.classPath.openFile(filename);
+ if (f == null)
+ global.error("could not read class " + c);
+ else {
+ new ClassfileParser(global, new AbstractFileReader(f), c).parse();
+ global.operation("loaded " + f.getPath() + " in " +
+ (System.currentTimeMillis() - msec) + "ms");
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ global.error("i/o error while loading " + c);
+ c.setInfo(Type.ErrorType);
+ }
+ }
+ }
+}
+
diff --git a/sources/scalac/symtab/classfile/ClassfileConstants.java b/sources/scalac/symtab/classfile/ClassfileConstants.java
new file mode 100644
index 0000000000..16b9f5af44
--- /dev/null
+++ b/sources/scalac/symtab/classfile/ClassfileConstants.java
@@ -0,0 +1,57 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.util.Name;
+
+public interface ClassfileConstants {
+
+ int JAVA_MAGIC = 0xCAFEBABE;
+ int JAVA_MAJOR_VERSION = 45;
+ int JAVA_MINOR_VERSION = 3;
+
+ int CONSTANT_UTF8 = 1;
+ int CONSTANT_UNICODE = 2;
+ int CONSTANT_INTEGER = 3;
+ int CONSTANT_FLOAT = 4;
+ int CONSTANT_LONG = 5;
+ int CONSTANT_DOUBLE = 6;
+ int CONSTANT_CLASS = 7;
+ int CONSTANT_STRING = 8;
+ int CONSTANT_FIELDREF = 9;
+ int CONSTANT_METHODREF = 10;
+ int CONSTANT_INTFMETHODREF = 11;
+ int CONSTANT_NAMEANDTYPE = 12;
+
+ int BAD_ATTR = 0x00000;
+ int SOURCEFILE_ATTR = 0x00001;
+ int SYNTHETIC_ATTR = 0x00002;
+ int DEPRECATED_ATTR = 0x00004;
+ int CODE_ATTR = 0x00008;
+ int EXCEPTIONS_ATTR = 0x00010;
+ int CONSTANT_VALUE_ATTR = 0x00020;
+ int LINE_NUM_TABLE_ATTR = 0x00040;
+ int LOCAL_VAR_TABLE_ATTR = 0x00080;
+ int INNERCLASSES_ATTR = 0x08000;
+ int META_ATTR = 0x10000;
+ int SCALA_ATTR = 0x20000;
+
+ Name SOURCEFILE_N = Name.fromString("SourceFile");
+ Name SYNTHETIC_N = Name.fromString("Synthetic");
+ Name DEPRECATED_N = Name.fromString("Deprecated");
+ Name CODE_N = Name.fromString("Code");
+ Name EXCEPTIONS_N = Name.fromString("Exceptions");
+ Name CONSTANT_VALUE_N = Name.fromString("ConstantValue");
+ Name LINE_NUM_TABLE_N = Name.fromString("LineNumberTable");
+ Name LOCAL_VAR_TABLE_N = Name.fromString("LocalVariableTable");
+ Name INNERCLASSES_N = Name.fromString("InnerClasses");
+ Name META_N = Name.fromString("JacoMeta");
+ Name SCALA_N = Name.fromString("ScalaSignature");
+ Name CONSTR_N = Name.fromString("<init>");
+}
diff --git a/sources/scalac/symtab/classfile/ClassfileParser.java b/sources/scalac/symtab/classfile/ClassfileParser.java
new file mode 100644
index 0000000000..956c83968f
--- /dev/null
+++ b/sources/scalac/symtab/classfile/ClassfileParser.java
@@ -0,0 +1,239 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.*;
+import scalac.util.*;
+import scalac.symtab.*;
+import java.io.*;
+import java.util.*;
+
+//todo: don't keep statics module in scope.
+
+public class ClassfileParser implements ClassfileConstants {
+
+ static final int CLASS_ATTR = SOURCEFILE_ATTR
+ | INNERCLASSES_ATTR
+ | SYNTHETIC_ATTR
+ | DEPRECATED_ATTR
+ | META_ATTR
+ | SCALA_ATTR;
+ static final int METH_ATTR = CODE_ATTR
+ | EXCEPTIONS_ATTR
+ | SYNTHETIC_ATTR
+ | DEPRECATED_ATTR
+ | META_ATTR;
+ static final int FIELD_ATTR = CONSTANT_VALUE_ATTR
+ | SYNTHETIC_ATTR
+ | DEPRECATED_ATTR
+ | META_ATTR;
+
+ protected Global global;
+ protected AbstractFileReader in;
+ protected Symbol c;
+ protected Type ctype;
+ protected Scope locals;
+ protected Scope statics;
+ protected Scope constr;
+ protected JavaTypeFactory make;
+ protected Signatures sigs;
+ protected ConstantPool pool;
+ protected AttributeParser attrib;
+ protected Definitions defs;
+ protected int phaseId;
+
+
+ public ClassfileParser(Global global, AbstractFileReader in, Symbol c) {
+ this.global = global;
+ this.in = in;
+ this.c = c;
+ this.ctype = Type.TypeRef(c.owner().thisType(), c, Type.EMPTY_ARRAY);
+ this.make = new JavaTypeCreator(global);
+ this.sigs = new Signatures(global, make);
+ this.pool = new ConstantPool(in, sigs);
+ this.attrib = new AttributeParser(in, pool, this);
+ this.defs = global.definitions;
+ this.phaseId = global.POST_ANALYZER_PHASE_ID;
+ }
+
+
+ /** parse the classfile and throw IO exception if there is an
+ * error in the classfile structure
+ */
+ public void parse() throws IOException {
+ try {
+ if (in.nextInt() != JAVA_MAGIC)
+ throw new IOException("illegal start of class file");
+ int minorVersion = in.nextChar();
+ int majorVersion = in.nextChar();
+ if ((majorVersion < JAVA_MAJOR_VERSION) ||
+ ((majorVersion == JAVA_MAJOR_VERSION) &&
+ (minorVersion < JAVA_MINOR_VERSION)))
+ throw new IOException("class file has wrong version " +
+ majorVersion + "." + minorVersion + ", should be " +
+ JAVA_MAJOR_VERSION + "." + JAVA_MINOR_VERSION);
+ pool.indexPool();
+ int flags = in.nextChar();
+ Name name = readClassName(in.nextChar());
+ if (c.fullName() != name)
+ throw new IOException("class file '" + c.fullName() +
+ "' contains wrong class " + name);
+ // todo: correct flag transition
+ c.flags = transFlags(flags);
+ if ((c.flags & Modifiers.ABSTRACT) != 0)
+ c.flags = c.flags & ~Modifiers.ABSTRACT | Modifiers.ABSTRACTCLASS;
+ Type supertpe = readClassType(in.nextChar());
+ Type[] basetpes = new Type[in.nextChar() + 1];
+ this.locals = new Scope();
+ this.statics = new Scope();
+ this.constr = new Scope();
+ // set type of class
+ Type classType = Type.compoundType(basetpes, locals, c);
+ c.setInfo(classType, phaseId);
+ // set type of statics
+ Symbol staticsClass = c.module().moduleClass();
+ Type staticsInfo = Type.compoundType(Type.EMPTY_ARRAY, statics, staticsClass);
+ staticsClass.setInfo(staticsInfo, phaseId);
+ c.module().setInfo(Type.TypeRef(staticsClass.owner().thisType(),
+ staticsClass, Type.EMPTY_ARRAY));
+ basetpes[0] = supertpe;
+ for (int i = 1; i < basetpes.length; i++)
+ basetpes[i] = readClassType(in.nextChar());
+ int fieldCount = in.nextChar();
+ for (int i = 0; i < fieldCount; i++)
+ parseField();
+ int methodCount = in.nextChar();
+ for (int i = 0; i < methodCount; i++)
+ parseMethod();
+ // set constructor type to the declared type
+ Symbol[] constrs = constr.elements();
+ if (constrs.length != 0) {
+ assert constrs.length == 1;
+ c.constructor().setInfo(constrs[0].info(), phaseId);
+ } else {
+ Type constrtype = ((c.flags & Modifiers.INTERFACE) != 0)
+ ? Type.PolyType(Symbol.EMPTY_ARRAY, ctype)
+ : Type.MethodType(new Symbol[]{Symbol.NONE}, ctype);
+ c.constructor().setInfo(constrtype, phaseId);
+ }
+ attrib.readAttributes(c, classType, CLASS_ATTR);
+ //System.out.println("dynamic class: " + c);
+ //System.out.println("statics class: " + staticsClass);
+ //System.out.println("module: " + c.module());
+ //System.out.println("modules class: " + c.module().type().symbol());
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ throw new IOException("bad class file (" + e.getMessage() + ")");
+ }
+ }
+
+ /** convert Java modifiers into Scala flags
+ */
+ public int transFlags(int flags) {
+ int res = 0;
+ if (((flags & 0x0007) == 0) ||
+ ((flags & 0x0002) != 0))
+ res |= Modifiers.PRIVATE;
+ else if ((flags & 0x0004) != 0)
+ res |= Modifiers.PROTECTED;
+ if ((flags & 0x0400) != 0)
+ res |= Modifiers.ABSTRACT;
+ if ((flags & 0x0010) != 0)
+ res |= Modifiers.FINAL;
+ if ((flags & 0x0200) != 0)
+ res |= Modifiers.INTERFACE | Modifiers.ABSTRACTCLASS;
+ return res | Modifiers.JAVA;
+ }
+
+ /** read a class name
+ */
+ protected Name readClassName(int i) {
+ return (Name)pool.readPool(i);
+ }
+
+ /** read a class name and return the corresponding class type
+ */
+ protected Type readClassType(int i) {
+ if (i == 0)
+ return defs.ANY_TYPE;
+ Type res = defs.getJavaType((Name)pool.readPool(i));
+ if (res == Type.ErrorType)
+ global.error("unknown class reference " + pool.readPool(i));
+ if (res.symbol() == defs.JAVA_OBJECT_TYPE.symbol())
+ return defs.ANYREF_TYPE;
+ else
+ return res;
+ }
+
+ /** read a signature and return it as a type
+ */
+ protected Type readType(int i) {
+ Name sig = pool.readExternal(i);
+ return sigs.sigToType(Name.names, sig.index, sig.length());
+ }
+
+ /** read a field
+ */
+ protected void parseField() {
+ int flags = in.nextChar();
+ Name name = (Name)pool.readPool(in.nextChar());
+ Type type = readType(in.nextChar());
+ int mods = transFlags(flags);
+ if ((flags & 0x0010) == 0)
+ mods |= Modifiers.MUTABLE;
+ Symbol owner = c;
+ if ((flags & 0x0008) != 0)
+ owner = c.module().moduleClass();
+ Symbol s = new TermSymbol(Position.NOPOS, name, owner, mods);
+ s.setInfo(type, phaseId);
+ attrib.readAttributes(s, type, FIELD_ATTR);
+ ((flags & 0x0008) != 0 ? statics : locals).enterOrOverload(s);
+ }
+
+ /** read a method
+ */
+ protected void parseMethod() {
+ int flags = in.nextChar();
+ Name name = (Name)pool.readPool(in.nextChar());
+ Type type = readType(in.nextChar());
+ if (CONSTR_N.equals(name)) {
+ Symbol s = TermSymbol.newConstructor(c, transFlags(flags));
+ // kick out protected, package visible or
+ // private constructors
+ if (((flags & 0x0004) != 0) ||
+ ((flags & 0x0002) != 0) ||
+ ((flags & 0x0007) == 0)) {
+ attrib.readAttributes(s, type, METH_ATTR);
+ return;
+ }
+ switch (type) {
+ case MethodType(Symbol[] vparams, _):
+ if (c == defs.OBJECT_CLASS)
+ type = Type.PolyType(Symbol.EMPTY_ARRAY, ctype);
+ else
+ type = Type.MethodType(vparams, ctype);
+ break;
+ default:
+ throw new ApplicationError();
+ }
+ s.setInfo(type, phaseId);
+ attrib.readAttributes(s, type, METH_ATTR);
+ //System.out.println("-- enter " + s);
+ constr.enterOrOverload(s);
+ } else {
+ Symbol s = new TermSymbol(
+ Position.NOPOS, name,
+ ((flags & 0x0008) != 0) ? c.module().moduleClass() : c,
+ transFlags(flags));
+ s.setInfo(type, phaseId);
+ attrib.readAttributes(s, type, METH_ATTR);
+ ((flags & 0x0008) != 0 ? statics : locals).enterOrOverload(s);
+ }
+ }
+}
diff --git a/sources/scalac/symtab/classfile/ConstantPool.java b/sources/scalac/symtab/classfile/ConstantPool.java
new file mode 100644
index 0000000000..4260b9e6ad
--- /dev/null
+++ b/sources/scalac/symtab/classfile/ConstantPool.java
@@ -0,0 +1,183 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.*;
+import scalac.symtab.*;
+import scalac.util.*;
+
+public class ConstantPool implements ClassfileConstants {
+
+ AbstractFileReader in;
+ Signatures sigparser;
+
+ /** the objects of the constant pool
+ */
+ protected Object[] poolObj;
+
+ /** for every constant pool entry, an index into in.buf where the
+ * defining section of the entry is found
+ */
+ protected int[] poolIdx;
+
+ /** constructor
+ */
+ protected ConstantPool(AbstractFileReader in, Signatures sigparser) {
+ this.in = in;
+ this.sigparser = sigparser;
+ }
+
+ /** index all constant pool entries, writing their start
+ * addresses into poolIdx
+ */
+ public void indexPool() {
+ poolIdx = new int[in.nextChar()];
+ poolObj = new Object[poolIdx.length];
+ int i = 1;
+ while (i < poolIdx.length) {
+ poolIdx[i++] = in.bp;
+ byte tag = in.nextByte();
+ switch (tag) {
+ case CONSTANT_UTF8:
+ case CONSTANT_UNICODE: {
+ int len = in.nextChar();
+ in.skip(len);
+ break;
+ }
+ case CONSTANT_CLASS:
+ case CONSTANT_STRING:
+ in.skip(2);
+ break;
+ case CONSTANT_FIELDREF:
+ case CONSTANT_METHODREF:
+ case CONSTANT_INTFMETHODREF:
+ case CONSTANT_NAMEANDTYPE:
+ case CONSTANT_INTEGER:
+ case CONSTANT_FLOAT:
+ in.skip(4);
+ break;
+ case CONSTANT_LONG:
+ case CONSTANT_DOUBLE:
+ in.skip(8);
+ i++;
+ break;
+ default:
+ throw new RuntimeException("bad constant pool tag: " + tag +
+ " at " + (in.bp - 1));
+ }
+ }
+ }
+
+ /** if name is an array type or class signature, return the
+ * corresponding type; otherwise return a class definition with given name
+ */
+ protected Object classOrType(Name name) {
+ if ((name.sub(0) == '[') || (name.sub(name.length() - 1) == ';')) {
+ byte[] ascii = name.toAscii();
+ return sigparser.sigToType(ascii, 0, ascii.length);
+ } else
+ return name;
+ }
+
+ /** read constant pool entry at start address i, use poolObj as a cache.
+ */
+ public Object readPool(int i) {
+ if (poolObj[i] != null)
+ return poolObj[i];
+ int index = poolIdx[i];
+ if (index == 0)
+ return null;
+ switch (in.byteAt(index)) {
+ case CONSTANT_UTF8:
+ poolObj[i] = Name.fromAscii(in.buf, index + 3, in.getChar(index + 1));
+ break;
+
+ case CONSTANT_UNICODE:
+ throw new RuntimeException("can't read unicode strings in classfiles");
+
+ case CONSTANT_CLASS:
+ poolObj[i] = classOrType(readExternal(in.getChar(index + 1)));
+ break;
+
+ case CONSTANT_FIELDREF: {
+ //Symbol owner = (Symbol)readPool(in.getChar(index + 1));
+ //NameAndType nt = (NameAndType)readPool(in.getChar(index + 3));
+ //poolObj[i] = new TermSymbol(Kinds.VAR, Position.NOPOS, nt.name, owner, 0)
+ // .type(sigparser.sigToType(Name.names, nt.sig.index, nt.sig.length()));
+ throw new RuntimeException("can't read constant_fieldrefs in classfiles");
+ }
+
+ case CONSTANT_METHODREF:
+ case CONSTANT_INTFMETHODREF: {
+ //Symbol owner = (Symbol)readPool(in.getChar(index + 1));
+ //NameAndType nt = (NameAndType)readPool(in.getChar(index + 3));
+ //poolObj[i] = new TermSymbol(Kinds.FUN, Position.NOPOS, nt.name, owner, 0)
+ // .type(sigparser.sigToType(Name.names, nt.sig.index, nt.sig.length()));
+ throw new RuntimeException("can't read constant_methodrefs in classfiles");
+ }
+
+ case CONSTANT_NAMEANDTYPE:
+ poolObj[i] = new NameAndType((Name)readPool(in.getChar(index + 1)),
+ readExternal(in.getChar(index + 3)));
+ break;
+
+ case CONSTANT_STRING:
+ case CONSTANT_INTEGER:
+ case CONSTANT_FLOAT:
+ case CONSTANT_LONG:
+ case CONSTANT_DOUBLE:
+ throw new RuntimeException("can't read constants in classfiles");
+
+ default:
+ throw new RuntimeException("bad constant pool tag: " + in.byteAt(index));
+ }
+ return poolObj[i];
+ }
+
+ /** return internal representation of buf[offset..offset+len-1],
+ * converting '/' to '.'
+ */
+ public byte[] internalize(byte[] buf, int offset, int len) {
+ byte[] translated = new byte[len];
+ for (int j = 0; j < len; j++) {
+ byte b = buf[offset + j];
+ if (b == '/')
+ translated[j] = '.';
+ else
+ translated[j] = b;
+ }
+ return translated;
+ }
+
+ /** read a constant pool string and convert to internal representation.
+ */
+ public Name readExternal(int i) {
+ if (poolObj[i] == null) {
+ int index = poolIdx[i];
+ if (in.byteAt(index) == CONSTANT_UTF8) {
+ int len = in.getChar(index + 1);
+ byte[] translated = internalize(in.buf, index + 3, len);
+ poolObj[i] = Name.fromAscii(translated, 0, len);
+ }
+ }
+ return (Name)poolObj[i];
+ }
+
+ /** the name and type signature of a method or field
+ */
+ public static final class NameAndType {
+ public Name name;
+ public Name sig;
+
+ public NameAndType(Name name, Name sig) {
+ this.name = name;
+ this.sig = sig;
+ }
+ }
+}
diff --git a/sources/scalac/symtab/classfile/JavaTypeCreator.java b/sources/scalac/symtab/classfile/JavaTypeCreator.java
new file mode 100644
index 0000000000..5bdc9402ba
--- /dev/null
+++ b/sources/scalac/symtab/classfile/JavaTypeCreator.java
@@ -0,0 +1,82 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.Global;
+import scalac.util.*;
+import scalac.symtab.*;
+import Type.*;
+
+
+public class JavaTypeCreator implements JavaTypeFactory {
+
+ protected Global global;
+
+ public JavaTypeCreator(Global global) {
+ this.global = global;
+ }
+
+ public Type byteType() {
+ return global.definitions.BYTE_TYPE;
+ }
+
+ public Type shortType() {
+ return global.definitions.SHORT_TYPE;
+ }
+
+ public Type charType() {
+ return global.definitions.CHAR_TYPE;
+ }
+
+ public Type intType() {
+ return global.definitions.INT_TYPE;
+ }
+
+ public Type longType() {
+ return global.definitions.LONG_TYPE;
+ }
+
+ public Type floatType() {
+ return global.definitions.FLOAT_TYPE;
+ }
+
+ public Type doubleType() {
+ return global.definitions.DOUBLE_TYPE;
+ }
+
+ public Type booleanType() {
+ return global.definitions.BOOLEAN_TYPE;
+ }
+
+ public Type voidType() {
+ return global.definitions.UNIT_TYPE;
+ }
+
+ public Type classType(Name classname) {
+ return global.definitions.getJavaType(classname);
+ }
+
+ public Type arrayType(Type elemtpe) {
+ return global.definitions.arrayType(elemtpe);
+ }
+
+ public Type methodType(Type[] argtpes, Type restpe, Type[] thrown) {
+ Symbol[] args = new Symbol[argtpes.length];
+ for (int i = 0; i < args.length; i++) {
+ args[i] = new TermSymbol(
+ Position.NOPOS, Name.fromString("x" + i), Symbol.NONE, Modifiers.PARAM);
+ args[i].setInfo(argtpes[i]);
+ }
+ return new MethodType(args, restpe);
+ }
+
+ public Type packageType(Name packagename) {
+ return null;
+ }
+}
diff --git a/sources/scalac/symtab/classfile/JavaTypeFactory.java b/sources/scalac/symtab/classfile/JavaTypeFactory.java
new file mode 100644
index 0000000000..8205055bc9
--- /dev/null
+++ b/sources/scalac/symtab/classfile/JavaTypeFactory.java
@@ -0,0 +1,28 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.symtab.Type;
+import scalac.util.Name;
+
+public interface JavaTypeFactory {
+ Type byteType();
+ Type shortType();
+ Type charType();
+ Type intType();
+ Type longType();
+ Type floatType();
+ Type doubleType();
+ Type booleanType();
+ Type voidType();
+ Type classType(Name classname);
+ Type arrayType(Type elemtpe);
+ Type methodType(Type[] argtpes, Type restpe, Type[] thrown);
+ Type packageType(Name packagename);
+}
diff --git a/sources/scalac/symtab/classfile/PackageParser.java b/sources/scalac/symtab/classfile/PackageParser.java
new file mode 100644
index 0000000000..50fcb46b00
--- /dev/null
+++ b/sources/scalac/symtab/classfile/PackageParser.java
@@ -0,0 +1,129 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.*;
+import scalac.symtab.*;
+import scalac.util.*;
+import java.io.*;
+
+public class PackageParser extends Type.LazyType {
+
+ /** the global compilation environment
+ */
+ protected Global global;
+
+ /** the class parser
+ */
+ public ClassParser classCompletion;
+
+ public PackageParser(Global global) {
+ this.global = global;
+ this.classCompletion = new ClassParser(global);
+ }
+
+ /** complete package type symbol p by loading all package members
+ */
+ public void complete(Symbol p) {
+ long msec = System.currentTimeMillis();
+ Scope members = new Scope();
+ String dirname = null;
+ Name name = p.fullName();
+ if (name.length() == 0) {
+ // includeMembers(AbstractFile.open(null, "."), p, members, false);
+ } else {
+ dirname = externalizeFileName(name);
+ assert !dirname.startsWith("com") : p;//debug
+ if (!dirname.endsWith("/"))
+ dirname += "/";
+ }
+ String[] base = global.classPath.components();
+ for (int i = 0; i < base.length; i++) {
+ includeMembers(
+ AbstractFile.open(base[i], dirname), p, members, dirname != null);
+ }
+ p.setInfo(Type.compoundType(Type.EMPTY_ARRAY, members, p));
+ if (dirname == null)
+ dirname = "anonymous package";
+ global.operation("scanned " + dirname + " in " +
+ (System.currentTimeMillis() - msec) + "ms");
+ }
+
+ /** read directory of a classpath directory and include members
+ * in package/module scope
+ */
+ protected void includeMembers(AbstractFile dir, Symbol p, Scope locals,
+ boolean inclClasses) {
+ if (dir == null)
+ return;
+ String[] filenames = null;
+ try {
+ if ((filenames = dir.list()) == null)
+ return;
+ for (int j = 0; j < filenames.length; j++) {
+ String fname = filenames[j];
+ if (inclClasses && fname.endsWith(".class")) {
+ Name n = Name.fromString(fname.substring(0, fname.length() - 6))
+ .toTypeName();
+ ClassSymbol clazz = new ClassSymbol(n, p, classCompletion);
+ clazz.constructor().setInfo(
+ classCompletion.staticsParser(clazz));
+ // enter class
+ locals.enter(clazz);
+ locals.enter(clazz.constructor());
+ // enter module, except for scala.Object class
+ // todo: why not there also?.
+ if (!(n == Names.Object.toTypeName() &&
+ p.fullName().toTermName() == Names.scala)) {
+ Scope.Entry e = locals.lookupEntry(clazz.module().name);
+ if (e != Scope.Entry.NONE) {
+ // we already have a package of the same name; delete it
+ locals.unlink(e);
+ }
+ locals.enter(clazz.module());
+ }
+ } else if (fname.endsWith("/") && !fname.equals("META-INF/")) {
+ Name n = Name.fromString(fname.substring(0, fname.length() - 1));
+ if (locals.lookup(n) == Symbol.NONE) {
+ TermSymbol module = TermSymbol.newJavaPackageModule(n, p, this);
+ locals.enter(module);
+ }
+ } else if (fname.endsWith(".scala")) {
+ Name n = Name.fromString(fname.substring(0, fname.length() - 6))
+ .toTypeName();
+ if (locals.lookup(n) == Symbol.NONE) {
+ SourceCompleter completer = new SourceCompleter(global,
+ dir.getPath() + File.separatorChar + fname);
+ ClassSymbol clazz = new ClassSymbol(n, p, completer);
+ clazz.constructor().setInfo(completer);
+ clazz.module().setInfo(completer);
+ // enter class
+ locals.enter(clazz);
+ locals.enter(clazz.constructor());
+ locals.enter(clazz.module());
+ }
+ }
+ }
+ } catch (IOException e) {
+ }
+ }
+
+ /** return external representation of file name s,
+ * converting '.' to File.separatorChar
+ */
+
+ public String externalizeFileName(Name n) {
+ if ((n == null) || (n.length() == 0))
+ return ".";
+ byte[] ascii = n.toAscii();
+ String s = SourceRepresentation.ascii2string(
+ ascii, 0, ascii.length);
+ return s.replace('.', File.separatorChar);
+ }
+}
diff --git a/sources/scalac/symtab/classfile/Signatures.java b/sources/scalac/symtab/classfile/Signatures.java
new file mode 100644
index 0000000000..9676882aed
--- /dev/null
+++ b/sources/scalac/symtab/classfile/Signatures.java
@@ -0,0 +1,122 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.symtab.classfile;
+
+import scalac.*;
+import scalac.symtab.*;
+import scalac.util.*;
+import java.util.*;
+import Type.*;
+
+public class Signatures {
+
+ /** signature constants
+ */
+ Name BYTE_SIG = Name.fromString("B");
+ Name SHORT_SIG = Name.fromString("S");
+ Name CHAR_SIG = Name.fromString("C");
+ Name INT_SIG = Name.fromString("I");
+ Name LONG_SIG = Name.fromString("J");
+ Name FLOAT_SIG = Name.fromString("F");
+ Name DOUBLE_SIG = Name.fromString("D");
+ Name BOOLEAN_SIG = Name.fromString("Z");
+ Name VOID_SIG = Name.fromString("V");
+ Name CLASS_SIG = Name.fromString("L");
+ Name ARRAY_SIG = Name.fromString("[");
+ Name ARGBEGIN_SIG = Name.fromString("(");
+ Name ARGEND_SIG = Name.fromString(")");
+
+ Global global;
+ JavaTypeFactory make;
+
+
+ public Signatures(Global global, JavaTypeFactory make) {
+ this.make = make;
+ this.global = global;
+ }
+
+ /** the type represented by signature[offset..].
+ */
+ protected byte[] signature;
+ protected int sigp;
+ protected int limit;
+
+ public Type sigToType(byte[] sig, int offset, int len) {
+ signature = sig;
+ sigp = offset;
+ limit = offset + len;
+ return sigToType();
+ }
+
+ protected Type sigToType() {
+ switch (signature[sigp]) {
+ case 'B':
+ sigp++;
+ return make.byteType();
+ case 'C':
+ sigp++;
+ return make.charType();
+ case 'D':
+ sigp++;
+ return make.doubleType();
+ case 'F':
+ sigp++;
+ return make.floatType();
+ case 'I':
+ sigp++;
+ return make.intType();
+ case 'J':
+ sigp++;
+ return make.longType();
+ case 'L':
+ sigp++;
+ int start = sigp;
+ while (signature[sigp] != ';')
+ sigp++;
+ return make.classType(Name.fromAscii(signature, start, (sigp++) - start));
+ case 'S':
+ sigp++;
+ return make.shortType();
+ case 'V':
+ sigp++;
+ return make.voidType();
+ case 'Z':
+ sigp++;
+ return make.booleanType();
+ case '[':
+ sigp++;
+ while (('0' <= signature[sigp]) && (signature[sigp] <= '9'))
+ sigp++;
+ return make.arrayType(sigToType());
+ case '(':
+ return make.methodType(sigToTypes(')'), sigToType(), Type.EMPTY_ARRAY);
+ default:
+ global.error("bad signature: " +
+ SourceRepresentation.ascii2string(signature, sigp, 1));
+ return Type.ErrorType;
+ }
+ }
+
+ protected Type[] sigToTypes(char terminator) {
+ sigp++;
+ return sigToTypes(terminator, 0);
+ }
+
+ protected Type[] sigToTypes(char terminator, int i) {
+ if (signature[sigp] == terminator) {
+ sigp++;
+ return new Type[i];
+ } else {
+ Type t = sigToType();
+ Type[] vec = sigToTypes(terminator, i+1);
+ vec[i] = t;
+ return vec;
+ }
+ }
+}
diff --git a/sources/scalac/transformer/LambdaLift.java b/sources/scalac/transformer/LambdaLift.java
new file mode 100644
index 0000000000..56cc92e6de
--- /dev/null
+++ b/sources/scalac/transformer/LambdaLift.java
@@ -0,0 +1,508 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.transformer;
+
+import java.io.*;
+import java.util.HashMap;
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import Tree.*;
+
+/** A lambda lifting transformer
+ *
+ * @author Martin Odersky
+ * @version 1.0
+ *
+ * What it does:
+ * Lift every class and function that's contained in another function
+ * out to be a member to the next enclosing class.
+ * Pass free variables and type variables as parameters to class constructors.
+ * Variables and values that are free in some function or class
+ * are given new unique names.
+ * Free mutable variables are converted to reference cells.
+ * All types are updated so that proxies are passed for additional free
+ * type variables.
+ */
+public class LambdaLift extends OwnerTransformer
+ implements Modifiers, Kinds {
+
+ final Global global;
+ final Definitions definitions;
+ final FreeVars free;
+ final LambdaLiftPhase descr;
+
+ public LambdaLift(Global global, LambdaLiftPhase descr) {
+ super(global, descr);
+ this.global = global;
+ this.definitions = global.definitions;
+ this.free = new FreeVars(global, descr);
+ this.descr = descr;
+ }
+
+ public void apply(Unit unit) {
+ global.log(unit.source.toString());
+ free.initialize(unit);
+ unit.body = transformTemplateStats(unit.body, currentOwner);
+ }
+
+ /** If `sym' is a class, return its primary constructor;
+ * otherwise current symbol itself
+ */
+ static Symbol asFunction(Symbol sym) {
+ return sym.kind == CLASS ? sym.constructor() : sym;
+ }
+
+ /** `asFunction' applied to the next enclosing function or class owner.
+ */
+ static Symbol enclFun(Symbol owner) {
+ Symbol sym = owner;
+ while (!asFunction(sym).isMethod()) sym = sym.owner();
+ return asFunction(sym);
+ }
+
+ /** Return SymSet from a hashmap.
+ */
+ private static SymSet get(HashMap f, Symbol sym) {
+ SymSet ss = (SymSet) f.get(sym);
+ return ss == null ? SymSet.EMPTY : ss;
+ }
+
+ /** Compute free variables map `fvs'.
+ * Also assign unique names to all
+ * value/variable/let symbols that are free in some function or class, and to
+ * all class/function symbols that are owned by some function.
+ */
+ static class FreeVars extends OwnerTransformer {
+
+ private Unit unit;
+
+ public FreeVars(Global global, PhaseDescriptor descr) {
+ super(global, descr);
+ }
+
+ /** A hashtable storing free variables of functions and class constructors.
+ */
+ private HashMap/*<Symbol,SymSet>*/ fvs;
+
+ /** A hashtable storing free type variables of functions and class constructors.
+ */
+ private HashMap/*<Symbol,SymSet>*/ ftvs;
+
+ /** A hashtable storing calls between functions and class constructors
+ */
+ private HashMap/*<Symbol,SymSet>*/ called;
+
+ /** The set of symbols that need to be renamed.
+ */
+ private SymSet renamable;
+
+ /** A flag to indicate whether new free variables have been found
+ */
+ private boolean changedFreeVars;
+
+ /** A flag to indicate whether we are in propagation phase
+ * (used in assertion).
+ */
+ private boolean propagatePhase;
+
+ /** Insert `sym' into the set of free variables of `owner'
+ */
+ private void putFree(Symbol owner, Symbol sym) {
+ assert owner.isLocal();
+ HashMap f = sym.isType() ? ftvs : fvs;
+ SymSet ss = get(f, owner);
+ if (!ss.contains(sym)) {
+ f.put(owner, ss.incl(sym));
+ changedFreeVars = true;
+ if (global.debug) global.log(sym + " is free in " + owner);
+ }
+ }
+
+ /** Insert `to' into the set of functions called by `from'
+ */
+ private void putCall(Symbol from, Symbol to) {
+ SymSet ss = get(called, from);
+ if (!ss.contains(to)) {
+ called.put(from, ss.incl(to));
+ }
+ }
+
+ /** 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.
+ */
+ private boolean markFree(Symbol sym, Symbol owner) {
+ //if (global.debug) global.log("mark " + sym + " free in " + owner);//DEBUG
+ if (owner.kind == NONE) {
+ assert propagatePhase : sym + " in " + sym.owner();
+ return false;
+ } else if (sym.owner() == owner) {
+ return true;
+ } else if (markFree(sym, owner.owner())) {
+ Symbol fowner = asFunction(owner);
+ if (fowner.isMethod()) {
+ putFree(fowner, sym);
+ renamable = renamable.incl(sym);
+ if (sym.isVariable()) sym.flags |= CAPTURED;
+ }
+ return owner.kind != CLASS;
+ } else {
+ return false;
+ }
+ }
+
+ /** Assign unique name to symbol.
+ * If symbol is a class assign same name to its primary constructor.
+ */
+ private void makeUnique(Symbol sym) {
+ Name newname = global.freshNameCreator.newName(sym.name);
+ sym.name = newname;
+ if (sym.kind == CLASS)
+ sym.constructor().name = newname.toConstrName();
+ }
+
+ private Type.Map traverseTypeMap = new Type.Map() {
+ public Type apply(Type tp) {
+ switch (tp) {
+ case TypeRef(ThisType(_), Symbol sym, Type[] targs):
+ if (sym.isLocal() && sym.kind == TYPE)
+ markFree(sym, currentOwner);
+ }
+ return map(tp);
+ }
+ };
+
+ public Tree transform(Tree tree) {
+ global.debugPrinter.print("free ").print(tree).println().end();//debug
+ traverseTypeMap.apply(tree.type.widen());
+ Symbol sym = tree.symbol();
+ switch(tree) {
+ case ClassDef(_, _, _, _, _, _):
+ case DefDef(_, _, _, _, _, _):
+ if (sym.isLocal()) {
+ renamable = renamable.incl(sym);
+ }
+ return super.transform(tree);
+
+ case Ident(Name name):
+ assert sym.owner() != null : sym + " " + name;//debug
+ if (sym.isLocal()) {
+ if (sym.isMethod()) {
+ Symbol f = enclFun(currentOwner);
+ if (f.name.length() > 0) // it is not a template function {
+ putCall(f, sym);
+ } else if (sym.kind == VAL || sym.kind == TYPE) {
+ markFree(sym, currentOwner);
+ }
+ }
+ return tree;
+
+ default:
+ return super.transform(tree);
+ }
+ }
+
+ /** Propagate free fariables from all called functions.
+ */
+ void propagateFvs(HashMap fvs) {
+ Object[] fs = called.keySet().toArray();
+ for (int i = 0; i < fs.length; i++) {
+ Symbol f = (Symbol) fs[i];
+ Symbol[] calledFromF = get(called, f).toArray();
+ for (int j = 0; j < calledFromF.length; j++) {
+ //System.out.println(f + " calls " + calledFromF[j]);//DEBUG
+ Symbol[] newFvs = get(fvs, calledFromF[j]).toArray();
+ for (int k = 0; k < newFvs.length; k++) {
+ markFree(newFvs[k], f);
+ }
+ }
+ }
+ }
+
+ /** This method re-enters all free variables into their free variable sets
+ * This is necessary because the names of these variables (and therefore their
+ * `isLess' order have changed.
+ */
+ void restoreFvs(HashMap fvs) {
+ Object[] fs = fvs.keySet().toArray();
+ for (int i = 0; i < fs.length; i++) {
+ Symbol f = (Symbol) fs[i];
+ Symbol[] elems = get(fvs, f).toArray();
+ SymSet elems1 = SymSet.EMPTY;
+ for (int j = 0; j < elems.length; j++)
+ elems1 = elems1.incl(elems[j]);
+ fvs.put(f, elems1);
+ }
+ }
+
+ /** Compute a mapping from symbols to their free variables
+ * in hashtable `fvs'. Also rename all variables that need it.
+ */
+ public void initialize(Unit unit) {
+ this.unit = unit;
+ fvs = new HashMap();
+ ftvs = new HashMap();
+ called = new HashMap();
+ renamable = SymSet.EMPTY;
+ apply(unit);
+
+ propagatePhase = true;
+ do {
+ changedFreeVars = false;
+ propagateFvs(fvs);
+ propagateFvs(ftvs);
+ } while (changedFreeVars);
+
+ Symbol[] ss = renamable.toArray();
+ for (int i = 0; i < ss.length; i++) {
+ makeUnique(ss[i]);
+ }
+
+ restoreFvs(fvs);
+ restoreFvs(ftvs);
+ }
+ }
+
+ private TreeList liftedDefs;
+
+ /** Transform template and add lifted definitions to it.
+ */
+ public Tree[] transformTemplateStats(Tree[] stats, Symbol tsym) {
+ TreeList prevLiftedDefs = liftedDefs;
+ liftedDefs = new TreeList();
+ TreeList stats1 = new TreeList(super.transformTemplateStats(stats, tsym));
+ stats1.append(liftedDefs);
+ liftedDefs = prevLiftedDefs;
+ return stats1.toArray();
+ }
+
+ public Tree transform(Tree tree) {
+ global.debugPrinter.print("lifting ").print(tree).println().end();//debug
+ tree.type = descr.transform(tree.type, currentOwner);
+ switch (tree) {
+ case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl):
+ Symbol sym = tree.symbol();
+ if (sym.isLocal()) {
+ Symbol[] newtparams = ftvsParams(sym.constructor());
+ Symbol[] newparams = fvsParams(sym.constructor());
+ liftSymbol(sym, newtparams, newparams);
+ Tree tree1 = copy.ClassDef(
+ tree, mods, sym.name,
+ addTypeParams(transform(tparams, sym), newtparams),
+ new ValDef[][]{addParams(transform(vparams, sym)[0], newparams)},
+ transform(tpe),
+ transform(impl, sym));
+ liftedDefs.append(tree1);
+ return Tree.Empty;
+ } else {
+ return copy.ClassDef(
+ tree, mods, sym.name,
+ transform(tparams, sym), transform(vparams, sym), transform(tpe),
+ transform(impl, sym));
+ }
+
+ case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs):
+ Symbol sym = tree.symbol();
+ if (sym.isLocal()) {
+ Symbol[] newtparams = ftvsParams(sym);
+ Symbol[] newparams = fvsParams(sym);
+ liftSymbol(sym, newtparams, newparams);
+ Tree tree1 = copy.DefDef(
+ tree, mods, sym.name,
+ addTypeParams(transform(tparams, sym), newtparams),
+ new ValDef[][]{addParams(transform(vparams, sym)[0], newparams)},
+ transform(tpe),
+ transform(rhs, sym));
+ liftedDefs.append(tree1);
+ return Tree.Empty;
+ } else {
+ return copy.DefDef(
+ tree, mods, sym.name,
+ transform(tparams, sym), transform(vparams, sym), transform(tpe),
+ transform(rhs, sym));
+ }
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ Symbol sym = tree.symbol();
+ Name name1 = sym.name;
+ Tree tpe1 = transform(tpe);
+ Tree rhs1 = transform(rhs, sym);
+ if ((sym.flags & CAPTURED) != 0) {
+ assert sym.isLocal();
+ Type unboxedType = sym.typeAt(descr.nextPhase);
+ Type boxedType = descr.refType(unboxedType);
+ tpe1 = gen.mkType(tpe.pos, boxedType);
+ rhs1 = gen.New(
+ rhs.pos,
+ definitions.SCALA_TYPE,
+ boxedType.symbol(),
+ new Type[]{unboxedType},
+ new Tree[]{rhs1});
+ }
+ return copy.ValDef(tree, mods, name1, tpe1, rhs1);
+
+ case Apply(Tree fn, Tree[] args):
+ Symbol fsym = TreeInfo.methSymbol(fn);
+ Tree fn1 = transform(fn);
+ switch (fn1) {
+ case TypeApply(Tree fn2, Tree[] targs):
+ fn1 = copy.TypeApply(
+ fn1, fn2, addFreeArgs(tree.pos, get(free.ftvs, fsym), targs));
+ break;
+ default:
+ Tree[] targs = addFreeArgs(
+ tree.pos, get(free.ftvs, fsym), Tree.EMPTY_ARRAY);
+ if (targs.length > 0) fn1 = gen.TypeApply(fn1, targs);
+ }
+ Tree[] args1 = transform(args);
+ return copy.Apply(
+ tree, fn1, addFreeArgs(tree.pos, get(free.fvs, fsym), args1));
+
+ case Ident(Name name):
+ Symbol sym = tree.symbol();
+ Name name1 = sym.name;
+ if (sym.isLocal() &&
+ (sym.kind == TYPE || (sym.kind == VAL && !sym.isMethod()))) {
+ sym = descr.proxy(sym, currentOwner);
+ }
+ Tree tree1 = copy.Ident(tree, name1).setSymbol(sym).setType(
+ sym.typeAt(descr.nextPhase));
+ if ((sym.flags & CAPTURED) != 0) return gen.Select(tree1, Names.elem);
+ else return tree1;
+
+ default:
+ return super.transform(tree);
+ }
+ }
+
+ Symbol[] ftvsParams(Symbol owner) {
+ Symbol[] freevars = get(free.ftvs, owner).toArray();
+ Symbol[] params = new Symbol[freevars.length];
+ for (int i = 0; i < params.length; i++) {
+ params[i] = freevars[i].cloneSymbol();
+ params[i].pos = owner.pos;
+ params[i].flags = PARAM | SYNTHETIC;
+ params[i].setOwner(owner);
+ params[i].setInfo(freevars[i].type());
+ }
+ return params;
+ }
+
+ Symbol[] fvsParams(Symbol owner) {
+ Symbol[] freevars = get(free.fvs, owner).toArray();
+ Symbol[] params = new Symbol[freevars.length];
+ for (int i = 0; i < params.length; i++) {
+ params[i] = freevars[i].cloneSymbol();
+ params[i].pos = owner.pos;
+ params[i].flags &= CAPTURED;
+ params[i].flags |= PARAM | SYNTHETIC;
+ params[i].setOwner(owner);
+ params[i].setInfo(freevars[i].type());
+ }
+ return params;
+ }
+
+ /** change symbol so that
+ * owner = currentClass
+ * newparams are added
+ * enter symbol in scope of currentClass
+ */
+ void liftSymbol(Symbol sym, Symbol[] newtparams, Symbol[] newparams) {
+ Symbol enclClass = sym.owner().enclClass();
+ sym.setOwner(enclClass);
+ enclClass.members().enter(sym);
+ if (sym.isMethod()) {
+ if (newtparams.length != 0 || newparams.length != 0) {
+ sym.updateInfo(
+ addParams(
+ addTypeParams(sym.infoAt(descr.nextPhase), newtparams),
+ newparams));
+ if (global.debug)
+ global.log(sym + " has now type " + sym.typeAt(descr.nextPhase));
+ }
+ } else if (sym.kind == CLASS) {
+ liftSymbol(sym.constructor(), newtparams, newparams);
+ } else {
+ throw new ApplicationError();
+ }
+ }
+
+ Type addTypeParams(Type tp, Symbol[] newtparams) {
+ if (newtparams.length == 0) return tp;
+ switch (tp) {
+ case MethodType(_, _):
+ return Type.PolyType(newtparams, tp);
+ case PolyType(Symbol[] tparams, Type restpe):
+ Symbol[] tparams1 = new Symbol[tparams.length + newtparams.length];
+ System.arraycopy(tparams, 0, tparams1, 0, tparams.length);
+ System.arraycopy(newtparams, 0, tparams1, tparams.length, newtparams.length);
+ return Type.PolyType(tparams1, restpe);
+ default:
+ throw new ApplicationError("illegal type: " + tp);
+ }
+ }
+
+ Type addParams(Type tp, Symbol[] newparams) {
+ if (newparams.length == 0) return tp;
+ switch (tp) {
+ case MethodType(Symbol[] params, Type restpe):
+ Symbol[] params1 = new Symbol[params.length + newparams.length];
+ System.arraycopy(params, 0, params1, 0, params.length);
+ System.arraycopy(newparams, 0, params1, params.length, newparams.length);
+ return Type.MethodType(params1, restpe);
+ case PolyType(Symbol[] tparams, Type restpe):
+ return Type.PolyType(tparams, addParams(restpe, newparams));
+ default:
+ throw new ApplicationError("illegal type: " + tp);
+ }
+ }
+
+ TypeDef[] addTypeParams(TypeDef[] tparams, Symbol[] newtparams) {
+ if (newtparams.length == 0) return tparams;
+ TypeDef[] tparams1 = new TypeDef[tparams.length + newtparams.length];
+ System.arraycopy(tparams, 0, tparams1, 0, tparams.length);
+ for (int i = 0; i < newtparams.length; i++) {
+ tparams1[tparams.length + i] = (Tree.TypeDef)gen.TypeDef(newtparams[i]);
+ }
+ return tparams1;
+ }
+
+ ValDef[] addParams(ValDef[] params, Symbol[] newparams) {
+ if (newparams.length == 0) return params;
+ ValDef[] params1 = new ValDef[params.length + newparams.length];
+ System.arraycopy(params, 0, params1, 0, params.length);
+ for (int i = 0; i < newparams.length; i++) {
+ params1[params.length + i] = gen.Param(newparams[i]);
+ }
+ return params1;
+ }
+
+ /** For all variables or type variables in `fvs',
+ * append proxies to argument array `args'.
+ */
+ Tree[] addFreeArgs(int pos, SymSet fvs, Tree[] args) {
+ if (fvs != SymSet.EMPTY) {
+ Symbol[] fparams = fvs.toArray();
+ Tree[] args1 = new Tree[args.length + fparams.length];
+ System.arraycopy(args, 0, args1, 0, args.length);
+ for (int i = 0; i < fparams.length; i++) {
+ Symbol farg = descr.proxy(fparams[i], currentOwner);
+ args1[args.length + i] =
+ gen.Ident(pos, farg).setType(farg.typeAt(descr.nextPhase));
+ }
+ return args1;
+ } else {
+ return args;
+ }
+ }
+}
diff --git a/sources/scalac/transformer/LambdaLiftPhase.java b/sources/scalac/transformer/LambdaLiftPhase.java
new file mode 100644
index 0000000000..31d9eea2a8
--- /dev/null
+++ b/sources/scalac/transformer/LambdaLiftPhase.java
@@ -0,0 +1,135 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+\* */
+
+// $Id$
+
+package scalac.transformer;
+
+import scalac.*;
+import scalac.util.*;
+import scalac.parser.*;
+import scalac.symtab.*;
+import scalac.checkers.*;
+
+public class LambdaLiftPhase extends PhaseDescriptor implements Kinds, Modifiers {
+
+ private Global global;
+ int nextPhase;
+
+ public void initialize(Global global, int id) {
+ super.initialize(global, id);
+ this.global = global;
+ this.nextPhase = id + 1;
+ }
+
+ public String name () {
+ return "lambdalift";
+ }
+
+ public String description () {
+ return "lambda lifter";
+ }
+
+ public String taskDescription() {
+ return "lambda lifting";
+ }
+
+ public Phase createPhase(Global global) {
+ return new LambdaLift(global, this);
+ }
+
+ public Type transformInfo(Symbol sym, Type tp) {
+ Type tp1 = transform(tp, sym.owner());
+ if ((sym.flags & Modifiers.CAPTURED) != 0) return refType(tp1);
+ else return tp1;
+ }
+
+ /** Add proxies as type arguments for propagated type parameters.
+ */
+ Type transform(Type tp, Symbol owner) {
+ return transformTypeMap.setOwner(owner).apply(tp);
+ }
+
+ private class TransformTypeMap extends Type.Map {
+ Symbol owner;
+ Type.Map setOwner(Symbol owner) { this.owner = owner; return this; }
+
+ public Type apply(Type tp) {
+ switch (tp) {
+ case TypeRef(Type pre, Symbol sym, Type[] targs):
+ switch (pre) {
+ case ThisType(_):
+ if (sym.constructor().isUpdated(nextPhase)) {
+ System.out.println("updated: " + sym.constructor());//debug
+ Symbol[] tparams =
+ sym.constructor().infoAt(nextPhase).typeParams();
+ int i = tparams.length;
+ while (i > 0 && (tparams[i-1].flags & SYNTHETIC) != 0)
+ i--;
+ if (i < tparams.length) {
+ Type[] targs1 = new Type[tparams.length];
+ System.arraycopy(map(targs), 0, targs1, 0, targs.length);
+ while (i < tparams.length) {
+ targs1[i] = proxy(tparams[i], owner).type();
+ }
+ return Type.TypeRef(pre, sym, targs1);
+ }
+ }
+ }
+ break;
+ }
+ return map(tp);
+ }
+
+ /** All symbols are mapped to themselves.
+ */
+ public Scope map(Scope s) { return s; }
+ public Symbol map(Symbol s) { return s; }
+ public Symbol[] map(Symbol[] ss) { return ss; }
+ }
+
+ private TransformTypeMap transformTypeMap = new TransformTypeMap();
+
+ /** Return closest enclosing (type)parameter that has same name as `fv',
+ * or `fv' itself if this is the closest definition.
+ */
+ Symbol proxy(Symbol fv, Symbol owner) {
+ if (global.debug)
+ global.log("proxy " + fv + " in " + LambdaLift.asFunction(owner));
+ Symbol o = owner;
+ while (o.kind != NONE) {
+ Symbol fowner = LambdaLift.asFunction(o);
+ if (fv.owner() == fowner) return fv;
+ Type ft = (fowner.isUpdated(nextPhase)) ? fowner.typeAt(nextPhase)
+ : fowner.type();
+ Symbol[] ownerparams = fv.isType() ? ft.typeParams()
+ : ft.firstParams();
+ for (int i = 0; i < ownerparams.length; i++) {
+ if (ownerparams[i].name == fv.name)
+ return ownerparams[i];
+ }
+ o = o.owner();
+ }
+ throw new ApplicationError("proxy " + fv + " in " + owner);
+ }
+
+ /** The type scala.Ref[tp]
+ */
+ Type refType(Type tp) {
+ Symbol refClass = global.definitions.getClass(Names.scala_Ref);
+ assert refClass.kind == Kinds.CLASS;
+ return Type.TypeRef(global.definitions.SCALA_TYPE, refClass, new Type[]{tp});
+ }
+
+ public Checker[] postCheckers(Global global) {
+ return new Checker[] {
+ new CheckSymbols(global),
+ new CheckTypes(global),
+ new CheckOwners(global)
+ };
+ }
+}
diff --git a/sources/scalac/transformer/OwnerTransformer.java b/sources/scalac/transformer/OwnerTransformer.java
new file mode 100644
index 0000000000..c723aeb6bb
--- /dev/null
+++ b/sources/scalac/transformer/OwnerTransformer.java
@@ -0,0 +1,132 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.transformer;
+
+import java.io.*;
+import java.util.*;
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import Tree.*;
+
+
+/** A default transformer class which also maintains owner information
+ *
+ * @author Martin Odersky
+ * @version 1.0
+ */
+public class OwnerTransformer extends Transformer {
+
+ protected Symbol currentOwner;
+
+ public OwnerTransformer(Global global, PhaseDescriptor descr) {
+ super(global, descr);
+ }
+
+ public void apply(Unit unit) {
+ currentOwner = global.definitions.ROOT_CLASS;
+ unit.body = transform(unit.body);
+ }
+
+ public Tree transform(Tree tree, Symbol owner) {
+ Symbol prevOwner = currentOwner;
+ currentOwner = owner;
+ Tree tree1 = transform(tree);
+ currentOwner = prevOwner;
+ return tree1;
+ }
+
+ public TypeDef[] transform(TypeDef[] params, Symbol owner) {
+ Symbol prevOwner = currentOwner;
+ currentOwner = owner;
+ TypeDef[] res = transform(params);
+ currentOwner = prevOwner;
+ return res;
+ }
+
+ public ValDef[][] transform(ValDef[][] params, Symbol owner) {
+ Symbol prevOwner = currentOwner;
+ currentOwner = owner;
+ ValDef[][] res = transform(params);
+ currentOwner = prevOwner;
+ return res;
+ }
+
+ public Template transform(Template templ, Symbol owner) {
+ Symbol prevOwner = currentOwner;
+ if (owner.kind == Kinds.CLASS)
+ currentOwner = owner.constructor();
+ Tree[] parents1 = transform(templ.parents);
+ currentOwner = owner;
+ Tree[] body1 = transformTemplateStats(templ.body, templ.symbol());
+ currentOwner = prevOwner;
+ return copy.Template(templ, parents1, body1);
+ }
+
+ public Tree[] transformTemplateStats(Tree[] ts, Symbol tsym) {
+ Tree[] ts1 = ts;
+ for (int i = 0; i < ts.length; i++) {
+ Tree t = transformTemplateStat(ts[i], tsym);
+ if (t != ts[i] && ts1 == ts) {
+ ts1 = new Tree[ts.length];
+ System.arraycopy(ts, 0, ts1, 0, i);
+ }
+ ts1[i] = t;
+ }
+ return ts1;
+ }
+
+ public Tree transformTemplateStat(Tree stat, Symbol tsym) {
+ return transform(stat, tsym);
+ }
+
+ public Tree transform(Tree tree) {
+ switch(tree) {
+ case PackageDef(Tree packaged, Template impl):
+ return copy.PackageDef(
+ tree, transform(packaged), transform(impl, packaged.symbol()));
+
+ case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl):
+ return copy.ClassDef(
+ tree, mods, name,
+ transform(tparams, tree.symbol()),
+ transform(vparams, tree.symbol()),
+ transform(tpe),
+ transform(impl, tree.symbol()));
+
+ case ModuleDef(int mods, Name name, Tree tpe, Template impl):
+ return copy.ModuleDef(
+ tree, mods, name, transform(tpe),
+ transform(impl, tree.symbol().moduleClass()));
+
+ case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs):
+ return copy.DefDef(
+ tree, mods, name,
+ transform(tparams, tree.symbol()),
+ transform(vparams, tree.symbol()),
+ transform(tpe, tree.symbol()),
+ transform(rhs, tree.symbol()));
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ return copy.ValDef(
+ tree, mods, name, transform(tpe),
+ transform(rhs));
+
+ case TypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs):
+ return copy.TypeDef(
+ tree, mods, name,
+ transform(tparams, tree.symbol()),
+ transform(rhs, tree.symbol()));
+
+ default:
+ return super.transform(tree);
+ }
+ }
+}
diff --git a/sources/scalac/transformer/UnCurry.java b/sources/scalac/transformer/UnCurry.java
new file mode 100644
index 0000000000..21fe579130
--- /dev/null
+++ b/sources/scalac/transformer/UnCurry.java
@@ -0,0 +1,180 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.transformer;
+
+import java.io.*;
+import java.util.*;
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import Tree.*;
+
+/** Make all functions into one-argument functions
+ */
+public class UnCurry extends OwnerTransformer
+ implements Modifiers {
+
+ UnCurryPhase descr;
+
+ public UnCurry(Global global, UnCurryPhase descr) {
+ super(global, descr);
+ this.descr = descr;
+ }
+
+ /** (ps_1) ... (ps_n) => (ps_1, ..., ps_n)
+ */
+ ValDef[][] uncurry(ValDef[][] params) {
+ int n = 0;
+ for (int i = 0; i < params.length; i++)
+ n = n + params[i].length;
+ ValDef[] ps = new ValDef[n];
+ int j = 0;
+ for (int i = 0; i < params.length; i++) {
+ System.arraycopy(params[i], 0, ps, j, params[i].length);
+ j = j + params[i].length;
+ }
+ return new ValDef[][]{ps};
+ }
+
+ /** tree of non-method type T ==> same tree with method type ()T
+ */
+ Tree asMethod(Tree tree) {
+ switch (tree.type) {
+ case MethodType(_, _):
+ return tree;
+ default:
+ return tree.setType(Type.MethodType(Symbol.EMPTY_ARRAY, tree.type));
+ }
+ }
+
+ /** - 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: def x: T ==> x: () => T
+ * - for every use of a def-parameter: x ==> x.apply()
+ * - for every argument to a def parameter `def x: T':
+ * if argument is not a reference to a def parameter:
+ * convert argument `e' to (expansion of) `() => e'
+ */
+ public Tree transform(Tree tree) {
+ //uncurry type and symbol
+ if (tree.type != null) tree.type = descr.uncurry(tree.type);
+ switch (tree) {
+ case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl):
+ return copy.ClassDef(
+ tree, mods, name, tparams,
+ uncurry(transform(vparams, tree.symbol())),
+ tpe,
+ transform(impl, tree.symbol()));
+
+ case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs):
+ Tree rhs1 = transform(rhs, tree.symbol());
+ if (global.debug) global.log(name + ":" + rhs1.type);//debug
+ return copy.DefDef(
+ tree, mods, name, tparams,
+ uncurry(transform(vparams, tree.symbol())),
+ tpe, rhs1);
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ if (tree.symbol().isDefParameter()) {
+ int mods1 = mods & ~ DEF;
+ Type newtype = global.definitions.functionType(Type.EMPTY_ARRAY, tpe.type);
+ Tree tpe1 = gen.mkType(tpe.pos, newtype);
+ return copy.ValDef(tree, mods1, name, tpe1, rhs).setType(newtype);
+ } else {
+ return tree;
+ }
+
+ case TypeApply(Tree fn, Tree[] args):
+ Tree tree1 = asMethod(super.transform(tree));
+ return gen.Apply(tree1, new Tree[0]);
+
+ case Apply(Tree fn, Tree[] args):
+ // f(x)(y) ==> f(x, y)
+ // argument to parameterless function e => ( => e)
+ Type ftype = fn.type;
+ Tree fn1 = transform(fn);
+ Tree[] args1 = transformArgs(args, ftype);
+ switch (fn1) {
+ case Apply(Tree fn2, Tree[] args2):
+ Tree[] newargs = new Tree[args1.length + args2.length];
+ System.arraycopy(args2, 0, newargs, 0, args2.length);
+ System.arraycopy(args1, 0, newargs, args2.length, args1.length);
+ return copy.Apply(tree, fn2, newargs);
+ default:
+ return copy.Apply(tree, fn1, args1);
+ }
+
+ case Select(_, _):
+ case Ident(_):
+ Tree tree1 = super.transform(tree);
+ switch (tree1.symbol().type()) {
+ case PolyType(Symbol[] tparams, Type restp):
+ if (tparams.length == 0 && !(restp instanceof Type.MethodType)) {
+ return gen.Apply(asMethod(tree1), new Tree[0]);
+ } else {
+ return tree1;
+ }
+ default:
+ if (tree1.symbol().isDefParameter()) {
+ tree1.type = global.definitions.functionType(
+ Type.EMPTY_ARRAY, tree1.type);
+ return gen.Apply(gen.Select(tree1, Names.apply), new Tree[0]);
+ } else {
+ return tree1;
+ }
+ }
+
+ default:
+ return super.transform(tree);
+ }
+ }
+
+ /** Transform arguments `args' to method with type `methtype'.
+ */
+ private Tree[] transformArgs(Tree[] args, Type methtype) {
+ switch (methtype) {
+ case MethodType(Symbol[] params, _):
+ for (int i = 0; i < args.length; i++) {
+ args[i] = transformArg(args[i], params[i]);
+ }
+ return args;
+ case PolyType(_, Type restp):
+ return transformArgs(args, restp);
+ default:
+ throw new ApplicationError(methtype);
+ }
+ }
+
+ /** for every argument to a def parameter `def x: T':
+ * if argument is not a reference to a def parameter:
+ * convert argument `e' to (expansion of) `() => e'
+ */
+ private Tree transformArg(Tree arg, Symbol formal) {
+ Tree arg1 = transform(arg);
+ if ((formal.flags & DEF) != 0) {
+ Symbol sym = arg.symbol();
+ if (sym != null && (sym.flags & DEF) != 0) {
+ switch (arg1) {
+ case Apply(Select(Tree qual, Name name), Tree[] args1):
+ assert name == Names.apply && args1.length == 0;
+ return qual;
+ default:
+ global.debugPrinter.print(arg);//debug
+ throw new ApplicationError();
+ }
+ }
+ return transform(gen.mkUnitFunction(
+ arg, descr.uncurry(arg.type), currentOwner));
+ } else return arg1;
+ }
+}
diff --git a/sources/scalac/transformer/UnCurryPhase.java b/sources/scalac/transformer/UnCurryPhase.java
new file mode 100644
index 0000000000..cb80db1ee9
--- /dev/null
+++ b/sources/scalac/transformer/UnCurryPhase.java
@@ -0,0 +1,117 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.transformer;
+
+import scalac.*;
+import scalac.parser.*;
+import scalac.symtab.*;
+import scalac.typechecker.Infer;
+import scalac.checkers.*;
+
+public class UnCurryPhase extends PhaseDescriptor implements Modifiers {
+
+ private Global global;
+
+ public void initialize(Global global, int id) {
+ super.initialize(global, id);
+ this.global = global;
+ }
+
+ public String name () {
+ return "uncurry";
+ }
+
+ public String description () {
+ return "uncurry function types and applications";
+ }
+
+ public String taskDescription() {
+ return "uncurried";
+ }
+
+ public Phase createPhase(Global global) {
+ return new UnCurry(global, this);
+ }
+
+ /** - return symbol's transformed type,
+ * - if symbol is a def parameter with transformed type T, return () => T
+ */
+ public Type transformInfo(Symbol sym, Type tp0) {
+ Type tp1 = uncurry(tp0);
+ if (sym.isDefParameter()) return global.definitions.functionType(Type.EMPTY_ARRAY, tp1);
+ else return tp1;
+ }
+
+ /** - (ps_1)...(ps_n)T ==> (ps_1,...,ps_n)T
+ */
+ Type uncurry(Type tp) {
+ switch (tp) {
+ case MethodType(Symbol[] params, Type tp1):
+ Symbol[] uncurriedParams = uncurryParams(params);
+ Type uncurriedTp1 = uncurry(tp1);
+ switch (uncurriedTp1) {
+ case MethodType(Symbol[] params1, Type tp2):
+ Symbol[] newparams = new Symbol[uncurriedParams.length + params1.length];
+ System.arraycopy(uncurriedParams, 0, newparams, 0, uncurriedParams.length);
+ System.arraycopy(params1, 0, newparams, uncurriedParams.length, params1.length);
+ return Type.MethodType(newparams, tp2);
+ default:
+ if (uncurriedParams == params && uncurriedTp1 == tp1) return tp;
+ else return Type.MethodType(uncurriedParams, uncurriedTp1);
+ }
+ case PolyType(Symbol[] tparams, Type tp1):
+ if (tp instanceof Infer.VirtualPolyType)
+ return uncurry(tp1);
+ switch (tp1) {
+ case MethodType(_, _):
+ Type newtp1 = uncurry(tp1);
+ if (tp1 != newtp1) return Type.PolyType(tparams, newtp1);
+ else return tp;
+ default:
+ Type newtp1 = Type.MethodType(Symbol.EMPTY_ARRAY, tp1);
+ if (tparams.length == 0) return newtp1;
+ else return Type.PolyType(tparams, newtp1);
+ }
+ case OverloadedType(_, _):
+ return new Type.Map() {
+ public Type apply(Type t) { return uncurry(t); }
+ }.map(tp);
+ default:
+ return tp;
+ }
+ }
+
+ Symbol[] uncurryParams(Symbol[] params) {
+ Symbol[] params1 = params;
+ for (int i = 0; i < params.length; i++) {
+ Symbol param = params[i];
+ Symbol param1 = param;
+ Type tp = param.info();
+ Type tp1 = transformInfo(param, tp);
+ if (tp != tp1) {
+ if (params1 == params) {
+ params1 = new Symbol[params.length];
+ System.arraycopy(params, 0, params1, 0, i);
+ }
+ param1 = param.cloneSymbol().setType(tp1);
+ param1.flags &= ~DEF;
+ }
+ params1[i] = param1;
+ }
+ return params1;
+ }
+
+ public Checker[] postCheckers(Global global) {
+ return new Checker[] {
+ new CheckSymbols(global),
+ new CheckTypes(global),
+ new CheckOwners(global)
+ };
+ }
+}
diff --git a/sources/scalac/typechecker/Analyzer.java b/sources/scalac/typechecker/Analyzer.java
new file mode 100644
index 0000000000..5558239d1c
--- /dev/null
+++ b/sources/scalac/typechecker/Analyzer.java
@@ -0,0 +1,1975 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+// todo: (0) propagate target type in cast.
+// todo: (1) check that only stable defs override stable defs
+
+package scalac.typechecker;
+
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.ast.printer.*;
+import scalac.symtab.*;
+import Tree.*;
+import java.util.HashMap;
+import java.util.Vector;
+
+public class Analyzer extends Transformer implements Modifiers, Kinds {
+
+ private final Definitions definitions;
+ private final DeSugarize desugarize;
+ private final AnalyzerPhase descr;
+ final Infer infer;
+
+ public Analyzer(Global global, AnalyzerPhase descr) {
+ super(global, descr);
+ this.definitions = global.definitions;
+ this.descr = descr;
+ this.infer = new Infer(this);
+ this.desugarize = new DeSugarize(this, global);
+ }
+
+ /** Phase variables, used and set in transformers;
+ */
+ private Unit unit;
+ private Context context;
+ private Type pt;
+ private int mode;
+
+ public void apply() {
+ int errors = global.reporter.errors();
+ for (int i = 0; i < global.units.length; i++) {
+ enterUnit(global.units[i]);
+ }
+ super.apply();
+ int n = descr.newSources.size();
+ while (n > 0) {
+ int l = global.units.length;
+ Unit[] newUnits = new Unit[l + n];
+ System.arraycopy(global.units, 0, newUnits, 0, l);
+ for (int i = 0; i < n; i++)
+ newUnits[i + l] = (Unit)descr.newSources.get(i);
+ global.units = newUnits;
+ descr.newSources.clear();
+ for (int i = l; i < newUnits.length; i++) {
+ apply(newUnits[i]);
+ }
+ n = descr.newSources.size();
+ }
+ }
+
+ public void enterUnit(Unit unit) {
+ enter(new Context(Tree.Empty, descr.startContext), unit);
+ }
+
+ public void enter(Context context, Unit unit) {
+ assert this.unit == null : "start unit non null for " + unit;
+ this.unit = unit;
+ this.context = context;
+ descr.contexts.put(unit, context);
+ enterSyms(unit.body);
+ this.unit = null;
+ this.context = null;
+ }
+
+ public void lateEnter(Unit unit, Symbol sym) {
+ assert sym.pos == Position.NOPOS : sym;
+ enterUnit(unit);
+ if (sym.pos == Position.NOPOS) {
+ sym.setInfo(Type.ErrorType);
+ String kind;
+ if (sym.name.isTermName()) kind = "module or method ";
+ else if (sym.name.isTypeName()) kind = "class ";
+ else kind = "constructor ";
+ throw new Type.Error("file " + unit.source + " does not define public " +
+ kind + sym.name);
+ } else {
+ descr.newSources.add(unit);
+ }
+ }
+
+ public void apply(Unit unit) {
+ global.log("checking " + unit);
+ assert this.unit == null : "start unit non null for " + unit;
+ this.unit = unit;
+ this.context = (Context)descr.contexts.remove(unit);
+ assert this.context != null : "could not find context for " + unit;
+ //context.imports = context.outer.imports;
+ unit.body = transformStatSeq(unit.body, Symbol.NONE);
+ /** todo: check what this is for
+ if (global.target == global.TARGET_JAVA && unit.errors == 0) {
+ unit.symdata = new SymData(unit);
+ }
+ */
+ this.unit = null;
+ this.context = null;
+ global.operation("checked " + unit);
+ }
+
+ /** Mode constants
+ */
+ static final int NOmode = 0x000;
+ static final int EXPRmode = 0x001; // these 4 modes are mutually exclusive.
+ static final int PATTERNmode = 0x002;
+ static final int CONSTRmode = 0x004;
+ static final int TYPEmode = 0x008;
+
+ static final int FUNmode = 0x10; // orthogonal to above. When set
+ // we are looking for a method or constructor
+
+ static final int POLYmode = 0x020; // orthogonal to above. When set
+ // expression types can be polymorphic.
+
+ static final int QUALmode = 0x040; // orthogonal to above. When set
+ // expressions may be packages and
+ // Java statics modules.
+
+// Helper definitions ---------------------------------------------------------
+
+ /** The qualifier type of a potential application of the `match' method.
+ * or NoType, if this is something else.
+ */
+ private Type matchQualType(Tree fn) {
+ switch (fn) {
+ case Select(Tree qual, _):
+ if (fn.symbol() == definitions.OBJECT_TYPE.lookup(Names.match))
+ return qual.type.widen();
+ break;
+ case TypeApply(Tree fn1, _):
+ return matchQualType(fn1);
+ case Ident(_):
+ if (fn.symbol() == definitions.OBJECT_TYPE.lookup(Names.match))
+ return context.enclClass.owner.type();
+ break;
+ }
+ return fn.type == Type.ErrorType ? Type.ErrorType : Type.NoType;
+ }
+
+ private Tree deepCopy(Tree tree) {
+ switch (tree) {
+ case Ident(Name name):
+ return make.Ident(tree.pos, name)
+ .setSymbol(tree.symbol()).setType(tree.type);
+ case Select(Tree qual, Name name):
+ return make.Select(tree.pos, deepCopy(qual), name)
+ .setSymbol(tree.symbol()).setType(tree.type);
+ default:
+ return tree;
+ }
+ }
+
+ static Name value2TypeName(Object value) {
+ if (value instanceof Character) return Name.fromString("scala.Char");
+ else if (value instanceof Integer) return Name.fromString("scala.Int");
+ else if (value instanceof Long) return Name.fromString("scala.Long");
+ else if (value instanceof Float) return Name.fromString("scala.Float");
+ else if (value instanceof Double) return Name.fromString("scala.Double");
+ else if (value instanceof String) return Name.fromString("java.lang.String");
+ else throw new ApplicationError();
+ }
+
+ Tree error(Tree tree, String msg) {
+ unit.error(tree.pos, msg);
+ if (tree.hasSymbol()) tree = tree.setSymbol(Symbol.ERROR);
+ return tree.setType(Type.ErrorType);
+ }
+
+ void error(int pos, String msg) {
+ unit.error(pos, msg);
+ }
+
+ void typeError(int pos, Type found, Type req) {
+ String explanation = "";
+ switch (found) {
+ case MethodType(_, Type restype):
+ if (infer.isCompatible(restype, req))
+ explanation = "\n possible cause: missing arguments for method or constructor";
+ }
+ error(pos, infer.typeErrorMsg("type mismatch", found, req) + explanation);
+ }
+
+// Name resolution -----------------------------------------------------------
+
+ /** Is `sym' accessible as a member of tree `site' in current context?
+ */
+ boolean isAccessible(Symbol sym, Tree site) {
+ return
+ (sym.flags & (PRIVATE | PROTECTED)) == 0
+ ||
+ accessWithin(sym.owner())
+ ||
+ ((sym.flags & PRIVATE) == 0) &&
+ site.type.symbol().isSubClass(sym.owner()) &&
+ (site instanceof Tree.Super ||
+ isSubClassOfEnclosing(site.type.symbol()));
+ } //where
+
+ /** Are we inside definition of `owner'?
+ */
+ boolean accessWithin(Symbol owner) {
+ Context c = context;
+ while (c != Context.NONE && c.owner != owner) {
+ c = c.outer.enclClass;
+ }
+ return c != Context.NONE;
+ }
+
+ /** Is `clazz' a subclass of an enclosing class?
+ */
+ boolean isSubClassOfEnclosing(Symbol clazz) {
+ Context c = context;
+ while (c != Context.NONE && !clazz.isSubClass(c.owner)) {
+ c = c.outer.enclClass;
+ }
+ return c != Context.NONE;
+ }
+
+// Checking methods ----------------------------------------------------------
+
+ /** Check that symbol's definition is well-formed. This means:
+ * - no conflicting modifiers
+ * - def modifiers only in methods
+ * - declarations only in classes
+ * - classes with abstract members have `abstract' modifier.
+ * - symbols with `override' modifier override some other symbol.
+ */
+ void validate(Symbol sym) {
+ checkNoConflict(sym, ABSTRACT, PRIVATE);
+ checkNoConflict(sym, FINAL, PRIVATE);
+ checkNoConflict(sym, PRIVATE, PROTECTED);
+ checkNoConflict(sym, PRIVATE, OVERRIDE);
+ checkNoConflict(sym, ABSTRACT, FINAL);
+ if ((sym.flags & ABSTRACTCLASS) != 0 && sym.kind != CLASS) {
+ error(sym.pos, "`abstract' modifier can be used only for classes; " +
+ "\nit should be omitted for abstract members");
+ }
+ if ((sym.flags & OVERRIDE) != 0 && sym.kind == CLASS) {
+ error(sym.pos, "`override' modifier ot allowed for classes");
+ }
+ if ((sym.flags & DEF) != 0 && sym.owner().isPrimaryConstructor()) {
+ error(sym.pos, "`def' modifier not allowed for class parameters");
+ }
+ if ((sym.flags & ABSTRACT) != 0) {
+ if (sym.owner().kind != CLASS ||
+ (sym.owner().flags & MODUL) != 0 ||
+ sym.owner().isAnonymousClass()) {
+ error(sym.pos, abstractVarNote(sym,
+ "only classes can have declared but undefined members"));
+ sym.flags &= ~ABSTRACT;
+ }
+ }
+ if ((sym.flags & OVERRIDE) != 0) {
+ int i = -1;
+ if (sym.owner().kind == CLASS) {
+ Type[] parents = sym.owner().info().parents();
+ i = parents.length - 1;
+ while (i >= 0 &&
+ parents[i].lookupNonPrivate(sym.name).kind == NONE)
+ i--;
+ }
+ if (i < 0) {
+ error(sym.pos, sym + " overrides nothing");
+ sym.flags &= ~OVERRIDE;
+ }
+ }
+ }
+
+ /** Check that
+ * - all parents are class types
+ * - supertype conforms to supertypes of all mixin types.
+ * - final classes are only inherited by classes which are
+ * nested within definition of base class, or that occur within same
+ * statement sequence.
+ */
+ void validateParentClasses(Tree[] constrs, Type[] parents) {
+ if (parents.length == 0 || !checkClassType(constrs[0].pos, parents[0])) return;
+ for (int i = 1; i < parents.length; i++) {
+ if (!checkClassType(constrs[i].pos, parents[i])) return;
+ Type[] grandparents = parents[i].parents();
+ if (grandparents.length > 0 && !parents[0].isSubType(grandparents[0]))
+ error(constrs[i].pos, "illegal inheritance;\n " + parents[0] +
+ "does not conform to " + parents[i] + "'s supertype");
+ Symbol bsym = parents[i].symbol();
+ if ((bsym.flags & FINAL) != 0) {
+ // are we in same scope as base type definition?
+ Scope.Entry e = context.scope.lookupEntry(bsym.name);
+ if (e.sym != bsym || e.owner != context.scope) {
+ // we are not within same statement sequence
+ Context c = context;
+ while (c != Context.NONE && c.owner != bsym)
+ c = c.outer;
+ if (c == Context.NONE) {
+ error(constrs[i].pos, "illegal inheritance from final class");
+ }
+ }
+ }
+ }
+ }
+
+ /** Check that type is a class type.
+ */
+ private boolean checkClassType(int pos, Type tp) {
+ switch (tp.unalias()) {
+ case TypeRef(_, Symbol sym, _):
+ if (sym.kind == CLASS) return true;
+ else if (sym.kind == ERROR) return false;
+ break;
+ case ErrorType:
+ return false;
+ }
+ error(pos, "class type expected");
+ return false;
+ }
+
+ /** Check that type is an object type
+ */
+ private Type checkObjectType(int pos, Type tp) {
+ if (tp.isObjectType()) return tp;
+ else {
+ if (tp != Type.ErrorType) error(pos, "object type expected");
+ return Type.ErrorType;
+ }
+ }
+
+ /** 1. Check that only parameterless (uniform) classes are inherited several times.
+ * 2. Check that all type instances of an inherited uniform class are the same.
+ * 3. Check that case classes do not inherit from case classes.
+ */
+ void validateBaseTypes(Symbol clazz) {
+ if (clazz.type().parents().length > 1)
+ validateBaseTypes(clazz, clazz.type(),
+ new boolean[clazz.closure().length], 0);
+ }
+ //where
+ void validateBaseTypes(Symbol clazz, Type tp, boolean[] seen, int start) {
+ Symbol baseclazz = tp.symbol();
+ if (baseclazz.kind == CLASS) {
+ int index = clazz.closurePos(baseclazz);
+ if (seen[index]) {
+ // check that only uniform classes are inherited several times.
+ if (!clazz.isCompoundSym() && !baseclazz.isTrait()) {
+ error(clazz.pos, "illegal inheritance;\n" + clazz +
+ " inherits " + baseclazz + " twice");
+ }
+ // check no two different type instances of same class
+ // are inherited.
+ Type tp1 = clazz.closure()[index];
+ if (!tp1.isSameAs(tp)) {
+ if (clazz.isCompoundSym())
+ error(clazz.pos,
+ "illegal combination;\n " + "compound type " +
+ " combines different type instances of " +
+ baseclazz + ":\n" + tp + " and " + tp1);
+ else
+ error(clazz.pos, "illegal inheritance;\n " + clazz +
+ " inherits different type instances of " +
+ baseclazz + ":\n" + tp + " and " + tp1);
+ }
+ }
+ // check that case classes do not inherit from case classes
+ if (clazz.isCaseClass() && baseclazz.isCaseClass())
+ error(clazz.pos, "illegal inheritance;\n " + "case " + clazz +
+ "inherits from other case " + baseclazz);
+
+ seen[index] = true;
+ Type[] parents = tp.parents();
+ for (int i = parents.length - 1; i >= start; i--) {
+ validateBaseTypes(clazz, parents[i].unalias(), seen, i == 0 ? 0 : 1);
+ }
+ }
+ }
+
+ /** Check that found type conforms to required one.
+ */
+ Type checkType(int pos, Type found, Type required) {
+ if (found.isSubType(required)) return found;
+ else {
+ typeError(pos, found, required);
+ if (global.debug) {
+ Type.debugSwitch = true;
+ found.isSubType(required);
+ Type.debugSwitch = false;
+ }
+ return Type.ErrorType;
+ }
+ }
+
+ /** Check that type is eta-expandable (i.e. no `def' parameters)
+ */
+ void checkEtaExpandable(int pos, Type tp) {
+ switch (tp) {
+ case MethodType(Symbol[] params, Type restype):
+ for (int i = 0; i < params.length; i++) {
+ if ((params[i].flags & DEF) != 0)
+ error(pos, "method with `def' parameters needs to be fully applied");
+ }
+ checkEtaExpandable(pos, restype);
+ }
+ }
+
+ /** Check that `sym' does not contain both `flag1' and `flag2'
+ */
+ void checkNoConflict(Symbol sym, int flag1, int flag2) {
+ if ((sym.flags & (flag1 | flag2)) == (flag1 | flag2)) {
+ if (flag1 == ABSTRACT)
+ error(sym.pos, "abstract member may not have " +
+ Modifiers.Helper.toString(flag2) + " modifier");
+ else
+ error(sym.pos, "illegal combination of modifiers: " +
+ Modifiers.Helper.toString(flag1) + " and " +
+ Modifiers.Helper.toString(flag2));
+ }
+ }
+
+ /** Check that
+
+ /** Check that type does not refer to components defined in current scope.
+ */
+ Type checkNoEscape(int pos, Type tp) {
+ try {
+ return checkNoEscapeMap.apply(tp);
+ } catch (Type.Error ex) {
+ error(pos, ex.msg);
+ return Type.ErrorType;
+ }
+ }
+ //where
+ private Type.Map checkNoEscapeMap = new Type.Map() {
+ public Type apply(Type t) {
+ switch (t.unalias()) {
+ case TypeRef(ThisType(_), Symbol sym, Type[] args):
+ Scope.Entry e = context.scope.lookupEntry(sym.name);
+ if (e.sym == sym && e.owner == context.scope) {
+ throw new Type.Error(
+ "type " + t + " escapes its defining scope");
+ } else {
+ map(args);
+ return t;
+ }
+ case SingleType(ThisType(_), Symbol sym):
+ Scope.Entry e = context.scope.lookupEntry(sym.name);
+ if (e.sym == sym && e.owner == context.scope) {
+ return apply(t.widen());
+ } else {
+ return t;
+ }
+ default:
+ return map(t);
+ }
+ }};
+
+ /** Check that tree represents a pure definition.
+ */
+ void checkPureDef(Tree tree, Symbol clazz) {
+ if (!TreeInfo.isPureDef(tree) && tree.type != Type.ErrorType)
+ error(tree.pos, clazz + " may contain only pure definitions");
+ }
+
+ /** Check that tree represents a pure definition.
+ */
+ void checkTrait(Tree tree, Symbol clazz) {
+ if (!TreeInfo.isPureConstr(tree) && tree.type != Type.ErrorType)
+ error(tree.pos, " " + clazz + " may inherit only from stable trait constructors");
+ }
+
+ /** Check that tree is a stable expression .
+ */
+ Tree checkStable(Tree tree) {
+ if (TreeInfo.isPureExpr(tree) || tree.type == Type.ErrorType) return tree;
+ new TextTreePrinter().print(tree).end();//debug
+ System.out.println(" " + tree.type);//debug
+ return error(tree, "stable identifier required");
+ }
+
+ /** Check all members of class `clazz' for overriding conditions.
+ */
+ void checkAllOverrides(Symbol clazz) {
+ Type[] closure = clazz.closure();
+ for (int i = 0; i < closure.length; i++) {
+ for (Scope.SymbolIterator it = closure[i].members().iterator();
+ it.hasNext();) {
+ Symbol other = it.next();
+ Symbol member = clazz.info().lookup(other.name);
+ if (other != member && member.kind != NONE)
+ checkOverride(clazz, member, other);
+ if ((member.flags & ABSTRACT) != 0 &&
+ clazz.kind == CLASS &&
+ (clazz.flags & ABSTRACTCLASS) == 0) {
+ if (clazz.isAnonymousClass())
+ error(clazz.pos, "object creation impossible, since " +
+ member + member.locationString() + " is not defined");
+ else
+ error(clazz.pos,
+ clazz + abstractVarNote(
+ member, " needs to be abstract; it does not define " +
+ member + member.locationString()));
+ clazz.flags |= ABSTRACTCLASS;
+ }
+ }
+ }
+ }
+
+ /** Check that all conditions for overriding `other' by `member' are met.
+ */
+ void checkOverride(Symbol clazz, Symbol member, Symbol other) {
+ int pos;
+ if (member.owner() == clazz) pos = member.pos;
+ else if (!member.owner().isSubClass(other.owner())) pos = context.tree.pos;
+ else return; // everything was already checked elsewhere
+
+ if ((member.flags & PRIVATE) != 0) {
+ overrideError(pos, member, other, "should not be private");
+ } else if ((other.flags & PROTECTED) != 0 && (member.flags & PROTECTED) == 0) {
+ overrideError(pos, member, other, "needs `protected' modifier");
+ } else if ((other.flags & FINAL) != 0) {
+ overrideError(pos, member, other, "cannot override final member");
+ } else if ((other.flags & ABSTRACT) == 0 && ((member.flags & OVERRIDE) == 0)) {
+ overrideError(pos, member, other, "needs `override' modifier");
+ } else {
+ Type self = clazz.thisType();
+ switch (other.kind) {
+ case CLASS:
+ overrideError(pos, member, other, "cannot override a class");
+ break;
+ case ALIAS:
+ if (!self.memberInfo(member).isSameAs(self.memberInfo(other)))
+ overrideTypeError(pos, member, other, self);
+ break;
+ default:
+ if (other.isConstructor())
+ overrideError(pos, member, other, "cannot override a class constructor");
+ if (!self.memberInfo(member).isSubType(self.memberInfo(other)))
+ overrideTypeError(pos, member, other, self);
+ }
+ }
+ }
+
+ void overrideError(int pos, Symbol member, Symbol other, String msg) {
+ if (other.type() != Type.ErrorType && member.type() != Type.ErrorType)
+ error(pos,
+ "error overriding " + other + other.locationString() +
+ "; " + member + member.locationString() + " " + msg);
+ }
+
+ void overrideTypeError(int pos, Symbol member, Symbol other, Type site) {
+ if (other.type() != Type.ErrorType && member.type() != Type.ErrorType)
+ error(pos,
+ member + member.locationString() +
+ infoString(member, site.memberInfo(member)) +
+ "\n cannot override " + other + other.locationString() +
+ infoString(other, site.memberInfo(other)));
+ }
+
+ String infoString(Symbol sym, Type symtype) {
+ switch (sym.kind) {
+ case ALIAS: return ", which equals " + symtype;
+ case TYPE: return " bounded by " + symtype;
+ case VAL: return " of type " + symtype;
+ default: return "";
+ }
+ }
+
+ String abstractVarNote(Symbol member, String msg) {
+ String note = ((member.flags & MUTABLE) == 0) ? ""
+ : "\n(Note that variables need to be initialized to be defined)";
+ return msg + note;
+ }
+
+// Entering Symbols ----------------------------------------------------------
+
+ /** If `tree' is a definition, create a symbol for it with a lazily
+ * constructed type, and enter into current scope.
+ */
+ Symbol enterSym(Tree tree) {
+ // todo: handle override qualifiers
+ Symbol owner = context.owner;
+ switch (tree) {
+ case PackageDef(Tree packaged, Tree.Template templ):
+ switch (templ) {
+ case Template(_, Tree[] body):
+ pushContext(tree, context.owner, context.scope);
+ context.imports = null;
+ ((PackageDef) tree).packaged = packaged =
+ transform(packaged, QUALmode);
+ popContext();
+ Symbol pkg = checkStable(packaged).symbol();
+ if (pkg != null && pkg.kind != ERROR) {
+ if (pkg.isPackage()) {
+ pushContext(templ, pkg.moduleClass(), pkg.members());
+ enterSyms(body);
+ popContext();
+ } else {
+ error(tree.pos, "only Java packages allowed for now");
+ }
+ }
+ templ.setSymbol(Symbol.NONE);
+ return null;
+ default:
+ throw new ApplicationError();
+ }
+
+ case ClassDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, _, Tree.Template templ):
+ ClassSymbol clazz = new ClassSymbol(tree.pos, name, owner, mods);
+ if (clazz.isLocalClass()) unit.mangler.setMangledName(clazz);
+ enterSym(tree, clazz.constructor());
+ if ((mods & CASE) != 0) {
+ // enter case constructor method.
+ enterInScope(
+ new TermSymbol(
+ tree.pos, name.toTermName(), owner, mods & (ACCESSFLAGS | CASE))
+ .setInfo(new LazyConstrMethodType(tree)));
+ }
+ return enterSym(tree, clazz);
+
+ case ModuleDef(int mods, Name name, _, _):
+ TermSymbol modul = TermSymbol.newModule(tree.pos, name, owner, mods);
+ Symbol clazz = modul.moduleClass();
+ clazz.setInfo(new LazyTreeType(tree));
+ if (clazz.isLocalClass()) unit.mangler.setMangledName(clazz);
+ return enterSym(tree, modul);
+
+ case ValDef(int mods, Name name, _, _):
+ return enterSym(tree, new TermSymbol(tree.pos, name, owner, mods));
+
+ case DefDef(int mods, Name name, _, _, _, _):
+ return enterSym(tree, new TermSymbol(tree.pos, name, owner, mods));
+
+ case TypeDef(int mods, Name name, _, _):
+ int kind = (mods & (ABSTRACT | PARAM)) != 0 ? TYPE : ALIAS;
+ TypeSymbol tsym = new TypeSymbol(kind, tree.pos, name, owner, mods);
+ if (kind == ALIAS)
+ tsym.constructor().setInfo(new LazyTreeType(tree));
+ return enterSym(tree, tsym);
+
+ case Import(Tree expr, Name[] selectors):
+ return enterImport(tree,
+ new TermSymbol(
+ tree.pos,
+ Name.fromString("import " + expr),
+ Symbol.NONE, SYNTHETIC));
+
+ default:
+ return null;
+ }
+ }//where
+
+ /** Enter `sym' in current scope and make it the symbol of `tree'.
+ */
+ private Symbol enterSym(Tree tree, Symbol sym) {
+ //if (global.debug) System.out.println("entering " + sym);//DEBUG
+ sym.setInfo(new LazyTreeType(tree));
+ sym = enterInScope(sym);
+ tree.setSymbol(sym);
+ return sym;
+ }
+
+ /** Make `sym' the symbol of import `tree' and create an entry in
+ * current imports list.
+ */
+ private Symbol enterImport(Tree tree, Symbol sym) {
+ sym.setInfo(new LazyTreeType(tree));
+ tree.setSymbol(sym);
+ context.imports = new ImportList(tree, context.scope, context.imports);
+ return sym;
+ }
+
+ /** Enter symbol `sym' in current scope. Check for double definitions.
+ * Handle overloading.
+ */
+ private Symbol enterInScope(Symbol sym) {
+ // handle double and overloaded definitions
+ Scope.Entry e = context.scope.lookupEntry(sym.name);
+ if (e.owner == context.scope) {
+ Symbol other = e.sym;
+ if (other.isPreloaded()) {
+ // symbol was preloaded from package;
+ // need to overwrite definition.
+ if (global.debug) System.out.println("overwriting " + other);//debug
+ sym.copyTo(other);
+ if (sym.isModule()) {
+ sym.moduleClass().copyTo(
+ other.moduleClass());
+ sym.moduleClass().constructor().copyTo(
+ other.moduleClass().constructor());
+ }
+ return other;
+ } else if (sym.kind == VAL && other.kind == VAL) {
+ // it's an overloaded definition
+ if (((sym.flags ^ other.flags) & SOURCEFLAGS) != 0) {
+ error(sym.pos,
+ "illegal overloaded definition of " + sym +
+ ": modifier lists differ in " +
+ Modifiers.Helper.toString(
+ (sym.flags ^ other.flags) & SOURCEFLAGS));
+ } else {
+ e.setSymbol(other.overloadWith(sym));
+ }
+ } else {
+ error(sym.pos,
+ sym.nameString() + " is already defined as " +
+ other + other.locationString());
+ }
+ } else {
+ context.scope.enter(sym);
+ }
+ return sym;
+ }
+
+ /** Enter all symbols in statement list
+ */
+ public void enterSyms(Tree[] stats) {
+ for (int i = 0; i < stats.length; i++)
+ enterSym(stats[i]);
+ }
+
+// Definining Symbols -------------------------------------------------------
+
+ /** Define symbol associated with `tree' using given `context'.
+ */
+ void defineSym(Tree tree, Unit unit, Infer infer, Context context) {
+ Unit savedUnit = this.unit;
+ this.unit = unit;
+ Context savedContext = this.context;
+ this.context = context;
+ int savedMode = this.mode;
+ this.mode = EXPRmode;
+ Type savedPt = this.pt;
+ this.pt = Type.AnyType;
+
+ Symbol sym = tree.symbol();
+ if (global.debug) System.out.println("defining " + sym);//debug
+ Type owntype;
+ switch (tree) {
+ case ClassDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, _, Tree.Template templ):
+ assert (mods & LOCKED) == 0 || sym.isAnonymousClass(): sym; // to catch repeated evaluations
+ ((ClassDef) tree).mods |= LOCKED;
+
+ if ((mods & CASE) != 0 && vparams.length > 0)
+ templ.body = desugarize.addCaseElements(templ.body, vparams[0]);
+
+ pushContext(tree, sym.constructor(), new Scope(context.scope));
+ Symbol[] tparamSyms = enterParams(tparams);
+ Symbol[][] vparamSyms = enterParams(vparams);
+ Type constrtype = makeMethodType(
+ tparamSyms,
+ vparamSyms,
+ Type.TypeRef(sym.owner().thisType(), sym, Symbol.type(tparamSyms)));
+ sym.constructor().setInfo(constrtype);
+ // necessary so that we can access tparams
+ sym.constructor().flags |= INITIALIZED;
+
+ defineTemplate(templ, sym);
+ owntype = templ.type;
+ popContext();
+ break;
+
+ case ModuleDef(int mods, Name name, Tree tpe, Tree.Template templ):
+ Symbol clazz = sym.moduleClass();
+ defineTemplate(templ, clazz);
+ clazz.setInfo(templ.type);
+ if (tpe == Tree.Empty) owntype = clazz.type();
+ else owntype = transform(tpe, TYPEmode).type;
+ break;
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ if (tpe == Tree.Empty) {
+ if (rhs == Tree.Empty) {
+ if ((sym.owner().flags & ACCESSOR) != 0) {
+ // this is the paremeter of a variable setter method.
+ ((ValDef) tree).tpe = tpe =
+ gen.mkType(tree.pos, sym.owner().accessed().type());
+ } else {
+ error(tree.pos, "missing parameter type");
+ ((ValDef) tree).tpe = tpe =
+ gen.mkType(tree.pos, Type.ErrorType);
+ }
+ owntype = tpe.type;
+ } else {
+ ((ValDef) tree).rhs = rhs = transform(rhs, EXPRmode);
+ owntype = rhs.type;
+ if ((sym.flags & MUTABLE) != 0) owntype = owntype.widen();
+ }
+ } else {
+ owntype = transform(tpe, TYPEmode).type;
+ }
+ break;
+
+ case DefDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, Tree tpe, Tree rhs):
+ pushContext(tree, sym, new Scope(context.scope));
+ Symbol[] tparamSyms = enterParams(tparams);
+ Symbol[][] vparamSyms = enterParams(vparams);
+ Type restpe;
+ if (tpe == Tree.Empty) {
+ int rhsmode = name.isConstrName() ? CONSTRmode : EXPRmode;
+ ((DefDef) tree).rhs = rhs = transform(rhs, rhsmode);
+ restpe = rhs.type;
+ } else {
+ restpe = transform(tpe, TYPEmode).type;
+ }
+ popContext();
+ owntype = makeMethodType(tparamSyms, vparamSyms, restpe);
+ break;
+
+ case TypeDef(int mods, Name name, Tree.TypeDef[] tparams, Tree rhs):
+ if (sym.kind == TYPE) {
+ pushContext(rhs, context.owner, context.scope);
+ this.context.delayArgs = true;
+ owntype = transform(rhs, TYPEmode).type;
+ owntype.symbol().initialize();//to detect cycles
+ popContext();
+ } else { // sym.kind == ALIAS
+ pushContext(tree, sym, new Scope(context.scope));
+ Symbol[] tparamSyms = enterParams(tparams);
+ sym.constructor().setInfo(Type.PolyType(tparamSyms, Type.NoType));
+ owntype = transform(rhs, TYPEmode).type;
+ popContext();
+ }
+ break;
+
+ case Import(Tree expr, Name[] selectors):
+ Tree expr1 = transform(expr, EXPRmode | QUALmode);
+ ((Import) tree).expr = expr1;
+ checkStable(expr1);
+ owntype = expr1.type;
+ break;
+
+ default:
+ throw new ApplicationError();
+ }
+ sym.setInfo(owntype);
+ validate(sym);
+ if (global.debug) System.out.println("defined " + sym);//debug
+ this.unit = savedUnit;
+ this.context = savedContext;
+ this.mode = savedMode;
+ this.pt = savedPt;
+ }
+
+ /** Definition phase for a template. This enters all symbols in template
+ * into symbol table.
+ */
+ void defineTemplate(Tree.Template templ, Symbol clazz) {
+ // attribute parent constructors
+ Tree[] constrs = transformConstrInvocations(
+ templ.pos, templ.parents, true, Type.AnyType);
+
+ Type[] parents = new Type[constrs.length];
+ for (int i = 0; i < parents.length; i++)
+ parents[i] = constrs[i].type;
+
+ // enter all members
+ Scope members = new Scope();
+ pushContext(templ, clazz, members);
+ if ((clazz.flags & CASE) != 0)
+ templ.body = desugarize.addCaseMethods(templ.body, clazz, parents);
+ templ.body = desugarize.Statements(templ.body, false);
+ enterSyms(templ.body);
+ popContext();
+
+ templ.type = Type.compoundType(parents, members, clazz);
+ }
+
+ Symbol[] enterParams(Tree[] params) {
+ enterSyms(params);
+ return Tree.symbolOf(params);
+ }
+
+ Symbol[][] enterParams(Tree[][] vparams) {
+ Symbol[][] vparamSyms = new Symbol[vparams.length][];
+ for (int i = 0; i < vparams.length; i++) {
+ vparamSyms[i] = enterParams(vparams[i]);
+ }
+ return vparamSyms;
+ }
+
+ Type makeMethodType(Symbol[] tparams, Symbol[][] vparams, Type restpe) {
+ if (tparams.length == 0 && vparams.length == 0) {
+ return Type.PolyType(tparams, restpe);
+ } else {
+ Type result = restpe;
+ for (int i = vparams.length - 1; i >= 0; i--)
+ result = Type.MethodType(vparams[i], result);
+ if (tparams.length != 0)
+ result = Type.PolyType(tparams, result);
+ return result;
+ }
+ }
+
+ /** Re-enter type parameters in current scope.
+ */
+ void reenterParams(Tree[] params) {
+ for (int i = 0; i < params.length; i++)
+ context.scope.enter(params[i].symbol());
+ }
+
+ /** Re-enter value parameters in current scope.
+ */
+ void reenterParams(Tree[][] vparams) {
+ for (int i = 0; i < vparams.length; i++)
+ reenterParams(vparams[i]);
+ }
+
+// Attribution and Transform -------------------------------------------------
+
+ /** 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.
+ */
+ Tree transformIdent(Tree tree, Name name) {
+ // find applicable definition and assign to `sym'
+ Symbol sym = Symbol.NONE;
+ Type pre;
+ Type symtype;
+
+ int stopPos = Integer.MIN_VALUE;
+ Context nextcontext = context;
+ while (sym.kind == NONE && nextcontext != Context.NONE) {
+ sym = nextcontext.scope.lookup(name);
+ if (sym.kind != NONE) {
+ stopPos = sym.pos;
+ } else {
+ nextcontext = nextcontext.enclClass;
+ if (nextcontext != Context.NONE) {
+ sym = nextcontext.owner.info().lookup(name);
+ if (sym.kind != NONE) {
+ stopPos = nextcontext.owner.pos;
+ } else {
+ nextcontext = nextcontext.outer;
+ }
+ }
+ }
+ }
+
+ // find applicable import and assign to `sym1'
+ ImportList nextimports = context.imports;
+ ImportList lastimports = null;
+ Symbol sym1 = Symbol.NONE;
+
+// System.out.println("name = " + name + ", pos = " + tree.pos + ", importlist = ");//DEBUG
+// for (ImportList imp = nextimports; imp != null; imp = imp.prev) {
+// new TextTreePrinter().print(" ").print(imp.tree).println().end();//debug
+// }
+
+ while (nextimports != null && nextimports.tree.pos >= tree.pos) {
+ nextimports = nextimports.prev;
+ }
+ while (sym1.kind == NONE &&
+ nextimports != null && nextimports.tree.pos > stopPos) {
+ sym1 = nextimports.importedSymbol(name);
+ lastimports = nextimports;
+ nextimports = nextimports.prev;
+ }
+
+ // evaluate what was found
+ if (sym1.kind == NONE) {
+ if (sym.kind == NONE) {
+ return error(tree, "not found: " + NameTransformer.decode(name));
+ } else {
+ sym.flags |= ACCESSED;
+ if (sym.owner().kind == CLASS)
+ pre = nextcontext.enclClass.owner.thisType();
+ else
+ pre = Type.localThisType;
+ }
+ } else if (sym.kind != NONE && !sym.isPreloaded()) {
+ return error(tree,
+ "reference to " + name + " is ambiguous;\n" +
+ "it is both defined in " + sym.owner() +
+ " and imported subsequently by \n" + nextimports.tree);
+ } else {
+ // check that there are no other applicable imports in same scope.
+ while (nextimports != null &&
+ nextimports.enclscope == lastimports.enclscope) {
+ if (!nextimports.sameImport(lastimports) &&
+ nextimports.importedSymbol(name).kind != NONE) {
+ return error(tree,
+ "reference to " + name + " is ambiguous;\n" +
+ "it is imported twice in the same scope by\n " +
+ lastimports.tree + "\nand " + nextimports.tree);
+ }
+ nextimports = nextimports.prev;
+ }
+ sym = sym1;
+ sym.flags |= (ACCESSED | SELECTOR);
+ Tree qual = checkStable(deepCopy(lastimports.importPrefix()));
+ pre = qual.type;
+ //new TextTreePrinter().print(name + " => ").print(lastimports.tree).print("." + name).println().end();//DEBUG
+ tree = make.Select(tree.pos, qual, name);
+ }
+ symtype = pre.memberType(sym);
+ if (sym.isTerm() && (sym.flags & MUTABLE) == 0 && symtype.isObjectType()) {
+ //System.out.println("making single " + sym + ":" + symtype);//DEBUG
+ symtype = Type.singleType(pre, sym);
+ }
+ //System.out.println(name + ":" + symtype);//DEBUG
+ return tree.setSymbol(sym).setType(symtype);
+ }
+
+ /** Attribute a selection where `tree' is `qual.name'.
+ * `qual' is already attributed.
+ */
+ Tree transformSelect(Tree tree, Tree qual, Name name) {
+ Symbol[] uninst = Symbol.EMPTY_ARRAY;
+ switch (qual.type) {
+ case PolyType(Symbol[] tparams, Type restype):
+ qual = infer.mkTypeApply(qual, tparams, restype, Symbol.type(tparams));
+ uninst = tparams;
+ }
+ Symbol sym = qual.type.lookup(name);
+ if (sym.kind == NONE) {
+ //System.out.println(qual.type + " has members " + qual.type.members());//DEBUG
+ return error(tree,
+ NameTransformer.decode(name) + " is not a member of " + qual.type.widen());
+ } else if (!isAccessible(sym, qual)) {
+ return error(tree, name + " cannot be accessed in " + qual.type.widen());
+ } else {
+ sym.flags |= (ACCESSED | SELECTOR);
+ Type symtype = qual.type.memberType(sym);
+ //System.out.println(sym.name + ":" + symtype);//debug
+ if (uninst.length != 0) {
+ switch (symtype) {
+ case PolyType(Symbol[] tparams, Type restype):
+ symtype = Type.PolyType(
+ tparams, new Infer.VirtualPolyType(uninst, restype));
+ break;
+ default:
+ symtype = new Infer.VirtualPolyType(uninst, symtype);
+ }
+ }
+ if (sym.isTerm() && (sym.flags & MUTABLE) == 0 && symtype.isObjectType() &&
+ qual.type.isStable())
+ symtype = Type.singleType(qual.type, sym);
+ return copy.Select(tree, qual, name)
+ .setSymbol(sym).setType(symtype);
+ }
+ }
+
+ /** Attribute a pattern matching expression where `pattpe' is the
+ * expected type of the patterns and `pt' is the expected type of the
+ * results.
+ */
+ Tree transformVisitor(Tree tree, Type pattpe, Type pt) {
+ //System.out.println("trans visitor with " + pt);//DEBUG
+ switch (tree) {
+ case Visitor(Tree.CaseDef[] cases):
+ Tree.CaseDef[] cases1 = cases;
+ for (int i = 0; i < cases.length; i++)
+ cases1[i] = transformCase(cases[i], pattpe, pt);
+ return copy.Visitor(tree, cases1)
+ .setType(Type.lub(Tree.typeOf(cases1)));
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** Attribute a case where `pattpe' is the expected type of the pattern
+ * and `pt' is the expected type of the result.
+ */
+ Tree.CaseDef transformCase(Tree.CaseDef tree, Type pattpe, Type pt) {
+ switch (tree) {
+ case CaseDef(Tree pat, Tree guard, Tree body):
+ pushContext(tree, context.owner, new Scope(context.scope));
+ Tree pat1 = transform(pat, PATTERNmode, pattpe);
+ Tree guard1 = guard;
+ if (guard != Tree.Empty)
+ guard1 = transform(guard, EXPRmode, definitions.BOOLEAN_TYPE);
+ Tree body1 = transform(body, EXPRmode, pt);
+ popContext();
+ return (Tree.CaseDef) copy.CaseDef(tree, pat1, guard1, body1)
+ .setType(body1.type);
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ Tree[] transformStatSeq(Tree[] stats, Symbol exprOwner) {
+ Tree[] stats1 = stats;
+ for (int i = 0; i < stats.length; i++) {
+ Tree stat = stats[i];
+ if (context.owner.isCompoundSym() && !TreeInfo.isDeclaration(stat)) {
+ error(stat.pos, "only declarations allowed here");
+ }
+ Tree stat1;
+ if (exprOwner.kind != NONE && !TreeInfo.isOwnerDefinition(stat)) {
+ pushContext(stat, exprOwner, context.scope);
+ if (TreeInfo.isDefinition(stat)) stat1 = transform(stat);
+ else stat1 = transform(stat, EXPRmode);
+ popContext();
+ } else {
+ if (TreeInfo.isDefinition(stat)) stat1 = transform(stat);
+ else stat1 = transform(stat, EXPRmode);
+ }
+ if (stat1 != stat && stats1 == stats) {
+ stats1 = new Tree[stats.length];
+ System.arraycopy(stats, 0, stats1, 0, i);
+ }
+ stats1[i] = stat1;
+ }
+ return stats1;
+ }
+
+ /** Attribute a sequence of constructor invocations.
+ */
+ Tree[] transformConstrInvocations(int pos, Tree[] constrs,
+ boolean delayArgs, Type pt) {
+ for (int i = 0; i < constrs.length; i++) {
+ pushContext(constrs[i], context.owner, context.scope);
+ context.delayArgs = delayArgs;
+ constrs[i] = transform(constrs[i], CONSTRmode, pt);
+ if (constrs[i].hasSymbol())
+ constrs[i].symbol().initialize();//to detect cycles
+ popContext();
+ }
+ return constrs;
+ }
+
+ /** Attribute a template
+ */
+ public Tree.Template transformTemplate(Tree.Template templ, Symbol owner) {
+ //System.out.println("transforming " + owner);//DEBUG
+ //System.out.println(owner.info());//DEBUG
+ Tree[] parents1 = transformConstrInvocations(
+ templ.pos, templ.parents, false, Type.AnyType);
+ if (owner.kind != ERROR) {
+ validateParentClasses(templ.parents, owner.info().parents());
+ validateBaseTypes(owner);
+ }
+ pushContext(templ, owner, owner.members());
+ templ.setSymbol(gen.localDummy(templ.pos, owner));
+ Tree[] body1 = transformStatSeq(templ.body, templ.symbol());
+ checkAllOverrides(owner);
+ popContext();
+ if (owner.isTrait()) {
+ for (int i = 0; i < templ.parents.length; i++)
+ checkTrait(templ.parents[i], owner);
+ for (int i = 0; i < templ.body.length; i++)
+ checkPureDef(templ.body[i], owner);
+ }
+ Tree.Template templ1 = copy.Template(templ, parents1, body1);
+ templ1.setType(owner.type());
+ return templ1;
+ }
+
+ public Tree transformApply(Tree tree, Tree fn, Tree[] args) {
+ Tree fn1;
+ int argMode;
+ if ((mode & (EXPRmode | CONSTRmode)) != 0) {
+ fn1 = transform(fn, mode | FUNmode, Type.AnyType);
+ argMode = EXPRmode;
+ } else {
+ assert (mode & PATTERNmode) != 0;
+ fn1 = transform(fn, mode | FUNmode, pt);
+ argMode = PATTERNmode;
+ }
+
+ // handle the case of application of match to a visitor specially
+ if (args.length == 1 && args[0] instanceof Visitor) {
+ Type pattp = matchQualType(fn1);
+ if (pattp == Type.ErrorType) {
+ return tree.setType(Type.ErrorType);
+ } else if (pattp != Type.NoType) {
+ Tree fn2 = desugarize.postMatch(fn1, context.enclClass.owner);
+ Tree arg1 = transformVisitor(args[0], pattp, pt);
+ return copy.Apply(tree, fn2, new Tree[]{arg1})
+ .setType(arg1.type);
+ }
+ }
+
+ // return prematurely if delayArgs is true and no type arguments
+ // need to be inferred.
+ if (context.delayArgs) {
+ switch (fn1.type) {
+ case MethodType(_, Type restp):
+ return tree.setType(restp);
+ }
+ }
+
+ // type arguments with formals as prototypes if they exist.
+ fn1.type = infer.freshInstance(fn1.type);
+ Type[] argtypes = transformArgs(
+ tree.pos, fn1.symbol(), Symbol.EMPTY_ARRAY, fn1.type, argMode, args, pt);
+
+ // propagate errors in arguments
+ if (argtypes == null) {
+ return tree.setType(Type.ErrorType);
+ }
+ for (int i = 0; i < argtypes.length; i++) {
+ if (argtypes[i] == Type.ErrorType) {
+ return tree.setType(Type.ErrorType);
+ }
+ }
+
+ // resolve overloading
+ switch (fn1.type) {
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ try {
+ infer.methodAlternative(fn1, alts, alttypes, argtypes, pt);
+ } catch (Type.Error ex) {
+ error(tree, ex.msg);
+ }
+ }
+
+ switch (fn1.type) {
+ case PolyType(Symbol[] tparams, Type restp):
+ // if method is polymorphic,
+ // infer instance, and adapt arguments to instantiated formals
+ try {
+ fn1 = infer.methodInstance(fn1, tparams, restp, argtypes);
+ } catch (Type.Error ex) {
+ error(tree, ex.msg);
+ }
+ switch (fn1.type) {
+ case MethodType(Symbol[] params, Type restp1):
+ for (int i = 0; i < args.length; i++) {
+ args[i] = adapt(args[i], argMode, params[i].type());
+ }
+ return copy.Apply(tree, fn1, args)
+ .setType(restp1);
+ }
+ break;
+ case MethodType(Symbol[] params, Type restp):
+ // if method is monomorphic,
+ // check that it can be applied to arguments.
+ if (infer.isApplicable(fn1.type, argtypes, Type.AnyType)) {
+ return copy.Apply(tree, fn1, args)
+ .setType(restp);
+ }
+ }
+
+ if (fn1.type == Type.ErrorType)
+ return tree.setType(Type.ErrorType);
+
+ new TextTreePrinter().print(tree).println().end();//debug
+ return error(tree,
+ infer.applyErrorMsg(
+ "", fn1, " cannot be applied to ", argtypes, pt));
+
+ }
+
+ /** Attribute an argument list.
+ * @param pos Position for error reporting
+ * @param meth The symbol of the called method, or `null' if none exists.
+ * @param tparams The type parameters that need to be instantiated
+ * @param methtype The method's type w/o type parameters
+ * @param argMode The argument mode (either EXPRmode or PATTERNmode)
+ * @param args The actual arguments
+ * @param pt The proto-resulttype.
+ * @return The vector of instantiated argument types, or null if error.
+ */
+ Type[] transformArgs(int pos, Symbol meth, Symbol[] tparams, Type methtype,
+ int argMode, Tree[] args, Type pt) {
+ //System.out.println("trans args " + meth + ArrayApply.toString(tparams) + ":" + methtype + "," + pt);//DEBUG
+ Type[] argtypes = new Type[args.length];
+ switch (methtype) {
+ case MethodType(Symbol[] params, Type restp):
+ if (params.length != args.length) {
+ error(pos, "wrong number of arguments" +
+ (meth == null ? "" : " for " + meth));
+ return null;
+ }
+ if (tparams.length == 0) {
+ for (int i = 0; i < args.length; i++) {
+ args[i] = transform(args[i], argMode, params[i].type());
+ argtypes[i] = args[i].type;
+ }
+ } else {
+ // targs: the type arguments inferred from the prototype
+ Type[] targs = infer.protoTypeArgs(tparams, restp, pt, params);
+
+ // argpts: prototypes for arguments
+ Type[] argpts = new Type[params.length];
+ for (int i = 0; i < params.length; i++)
+ argpts[i] = params[i].type().subst(tparams, targs);
+
+ // transform arguments with [targs/tparams]params.type as prototypes
+ for (int i = 0; i < args.length; i++)
+ args[i] = transform(
+ args[i], argMode | POLYmode,
+ params[i].type().subst(tparams, targs));
+
+ // targs1: same as targs except that every AnyType is mapped to
+ // formal parameter type.
+ Type[] targs1 = new Type[targs.length];
+ for (int i = 0; i < targs.length; i++)
+ targs1[i] = (targs[i] != Type.AnyType) ? targs[i]
+ : tparams[i].type();
+
+ for (int i = 0; i < args.length; i++) {
+ argtypes[i] = args[i].type;
+ switch (argtypes[i]) {
+ case PolyType(Symbol[] tparams1, Type restype1):
+ argtypes[i] = infer.argumentTypeInstance(
+ tparams1, restype1,
+ params[i].type().subst(tparams, targs1),
+ argpts[i]);
+ }
+ }
+ }
+ return argtypes;
+
+ case PolyType(Symbol[] tparams1, Type restp):
+ Symbol[] tparams2;
+ if (tparams.length == 0) tparams2 = tparams1;
+ else {
+ tparams2 = new Symbol[tparams.length + tparams1.length];
+ System.arraycopy(tparams, 0, tparams2, 0, tparams.length);
+ System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length);
+ }
+ return transformArgs(pos, meth, tparams2, restp, argMode, args, pt);
+
+ default:
+ for (int i = 0; i < args.length; i++) {
+ args[i] = transform(args[i], argMode, Type.AnyType);
+ argtypes[i] = args[i].type;
+ }
+ }
+ return argtypes;
+ }
+
+ /** Atribute an expression or pattern with prototype `pt'.
+ * Check that expression's type conforms to `pt'.
+ * Resolve overloading and apply parameterless functions.
+ * Insert `apply' function if needed.
+ */
+ Tree transform(Tree tree, int mode, Type pt) {
+ //new TextTreePrinter().print("transforming ").print(tree).println().end();//DEBUG
+ int savedMode = this.mode;
+ Type savedPt = this.pt;
+ this.mode = mode;
+ this.pt = pt;
+ Tree tree1 = adapt(transform(tree), mode, pt);
+ this.mode = savedMode;
+ this.pt = savedPt;
+ return tree1;
+ }
+
+ Tree adapt(Tree tree, int mode, Type pt) {
+ //new TextTreePrinter().print(tree).print(" adapt " + pt).println().end();//DEBUG
+ switch (tree.type) {
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ // resolve overloading
+ if ((mode & FUNmode) == 0) {
+ try {
+ infer.exprAlternative(tree, alts, alttypes, pt);
+ } catch (Type.Error ex) {
+ error(tree, ex.msg);
+ }
+ switch (tree.type) {
+ case OverloadedType(_, _):
+ // overload resolution failed bcs no alternative matched prototype.
+ typeError(tree.pos, tree.type, pt);
+ tree.setSymbol(Symbol.ERROR).setType(Type.ErrorType);
+ break;
+ default:
+ return adapt(tree, mode, pt);
+ }
+ }
+ break;
+
+ case PolyType(Symbol[] tparams, Type restp):
+ // apply parameterless functions
+ // instantiate polymorphic expressions
+ if (tparams.length == 0) {
+ return adapt(tree.setType(restp), mode, pt);
+ } else if ((mode & (FUNmode | POLYmode)) == 0) {
+ try {
+ tree = infer.exprInstance(tree, tparams, restp, pt);
+ } catch (Type.Error ex) {
+ error(tree, ex.msg);
+ }
+ return adapt(tree, mode, pt);
+ } else if ((mode & EXPRmode) != 0) {
+ // will be instantiated later
+ return tree;
+ }
+ break;
+
+ case MethodType(_, _):
+ // convert unapplied methods to functions.
+ if ((mode & (EXPRmode | FUNmode)) == EXPRmode &&
+ infer.isCompatible(tree.type, pt)) {
+ checkEtaExpandable(tree.pos, tree.type);
+ return transform(desugarize.etaExpand(tree, tree.type), mode, pt);
+ } else if ((mode & (CONSTRmode | FUNmode)) == CONSTRmode) {
+ return error(tree, "missing arguments for class constructor");
+ }
+ }
+ if ((mode & FUNmode) != 0) {
+ if ((mode & PATTERNmode) != 0) {
+ // set type to instantiated case class constructor
+ if (tree.type == Type.ErrorType) return tree;
+ Symbol clazz = tree.symbol().constructorClass();
+ if (!clazz.isCaseClass())
+ error(tree, clazz + " is not a case class");
+ tree.type = clazz.constructor().type();
+ switch (tree.type) {
+ case PolyType(Symbol[] tparams, Type restp):
+ try {
+ infer.constructorInstance(tree, tparams, restp, pt);
+ } catch (Type.Error ex) {
+ if (pt != Type.ErrorType) error(tree.pos, ex.msg);
+ tree.setType(Type.ErrorType);
+ }
+ }
+ return tree;
+ } else if ((mode & EXPRmode) != 0 && tree.type.isObjectType()) {
+ // insert apply method
+ Symbol applyMeth = tree.type.lookup(Names.apply);
+ if (applyMeth != Symbol.NONE && isAccessible(applyMeth, tree)) {
+ applyMeth.flags |= (ACCESSED | SELECTOR);
+ tree = make.Select(tree.pos, tree, Names.apply)
+ .setSymbol(applyMeth)
+ .setType(tree.type.memberType(applyMeth));
+ return adapt(tree, mode, pt);
+ }
+ }
+ } else if ((mode & (QUALmode | EXPRmode)) == EXPRmode) {
+ // check that packages and static modules are not used as values
+ Symbol sym = tree.symbol();
+ if (sym != null && sym.kind != ERROR && !sym.isValue() && tree.isTerm()) {
+ new TextTreePrinter().print(tree).println().end();//debug
+ error(tree.pos, tree.symbol() + " is not a value");
+ }
+ }
+
+ // check type against prototype
+ return tree.setType(checkType(tree.pos, tree.type, pt));
+ }
+
+ /** Transform expression or type with a given mode.
+ */
+ public Tree transform(Tree tree, int mode) {
+ if ((mode & TYPEmode) == 0)
+ return transform(tree, mode, Type.AnyType);
+
+ int savedMode = this.mode;
+ this.mode = mode;
+ Tree tree1 = transform(tree);
+ this.mode = savedMode;
+
+ Symbol sym = tree1.symbol();
+ if ((mode & FUNmode) == 0 && sym != null && sym.typeParams().length != 0)
+ return error(tree, sym + " takes type parameters.");
+ else
+ return tree1;
+ }
+
+ Tree[] transform(Tree[] trees, int mode) {
+ for (int i = 0; i < trees.length; i++)
+ trees[i] = transform(trees[i], mode);
+ return trees;
+ }
+
+ /** The main attribution function
+ */
+ public Tree transform(Tree tree) {
+ Symbol sym = tree.symbol();
+ if (sym != null && !sym.isInitialized()) sym.initialize();
+ if (global.debug && TreeInfo.isDefinition(tree))
+ System.out.println("transforming " + sym);
+ try {
+ switch (tree) {
+
+ case Bad():
+ tree.setType(Type.ErrorType);
+ return tree;
+
+ case Empty:
+ tree.type = Type.NoType;
+ return tree;
+
+ case PackageDef(Tree pkg, Tree.Template templ):
+ switch (templ) {
+ case Template(Tree[] parents, Tree[] body):
+ Symbol pkgSym = pkg.symbol();
+ if (pkgSym != null && pkgSym.isPackage()) {
+ pushContext(templ, pkgSym, pkgSym.members());
+ Tree[] body1 = transform(body);
+ popContext();
+ Tree.Template templ1 = copy.Template(templ, parents, body1);
+ templ1.setType(Type.NoType).setSymbol(Symbol.NONE);
+ return copy.PackageDef(tree, pkg, templ1)
+ .setType(definitions.UNIT_TYPE);
+ }
+ }
+ return tree.setType(Type.ErrorType);
+
+ case ClassDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, Tree tpe, Tree.Template templ):
+ pushContext(tree, sym.constructor(), new Scope(context.scope));
+ reenterParams(tparams);
+ reenterParams(vparams);
+ Tree.TypeDef[] tparams1 = transform(tparams);
+ Tree.ValDef[][] vparams1 = transform(vparams);
+ Tree tpe1 = transform(tpe);
+ Tree.Template templ1 = transformTemplate(templ, sym);
+ popContext();
+ return copy.ClassDef(tree, mods, name, tparams1, vparams1, tpe1, templ1)
+ .setType(definitions.UNIT_TYPE);
+
+ case ModuleDef(int mods, Name name, Tree tpe, Tree.Template templ):
+ Tree tpe1 = transform(tpe, TYPEmode);
+ Tree.Template templ1 = transformTemplate(templ, sym.moduleClass());
+ return copy.ModuleDef(tree, mods, name, tpe1, templ1)
+ .setType(definitions.UNIT_TYPE);
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ Tree tpe1 = transform(tpe, TYPEmode);
+ Tree rhs1 = rhs;
+ if (tpe1 == Tree.Empty) {
+ tpe1 = gen.mkType(rhs1.pos, rhs.type);
+ // rhs already attributed by defineSym in this case
+ } else if (rhs != Tree.Empty) {
+ rhs1 = transform(rhs1, EXPRmode, sym.type());
+ }
+ return copy.ValDef(tree, mods, name, tpe1, rhs1)
+ .setType(definitions.UNIT_TYPE);
+
+ case DefDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, Tree tpe, Tree rhs):
+ pushContext(tree, sym, new Scope(context.scope));
+ reenterParams(tparams);
+ reenterParams(vparams);
+ Tree.TypeDef[] tparams1 = transform(tparams);
+ Tree.ValDef[][] vparams1 = transform(vparams);
+ Tree tpe1 = transform(tpe, TYPEmode);
+ Tree rhs1 = rhs;
+ if (tpe1 == Tree.Empty) {
+ tpe1 = gen.mkType(rhs1.pos, rhs1.type);
+ // rhs already attributed by defineSym in this case
+ } else if (rhs != Tree.Empty) {
+ rhs1 = transform(rhs, EXPRmode,
+ tpe1.type == Type.NoType ? Type.AnyType : tpe1.type);
+ }
+ popContext();
+ return copy.DefDef(tree, mods, name, tparams1, vparams1, tpe1, rhs1)
+ .setType(definitions.UNIT_TYPE);
+
+ case TypeDef(int mods, Name name, Tree.TypeDef[] tparams, Tree rhs):
+ pushContext(tree, sym, new Scope(context.scope));
+ reenterParams(tparams);
+ Tree.TypeDef[] tparams1 = transform(tparams);
+ Tree rhs1 = transform(rhs, TYPEmode);
+ popContext();
+ return copy.TypeDef(tree, mods, name, tparams1, rhs1)
+ .setType(definitions.UNIT_TYPE);
+
+ case Import(Tree expr, Name[] selectors):
+ context.imports = new ImportList(tree, context.scope, context.imports);
+ return Tree.Empty;
+
+ case Block(Tree[] stats):
+ pushContext(tree, context.owner, new Scope(context.scope));
+ int lastmode = mode;
+ Tree[] stats1 = desugarize.Statements(stats, true);
+ enterSyms(stats1);
+ context.imports = context.outer.imports;
+ for (int i = 0; i < stats1.length - 1; i++)
+ stats1[i] = transform(stats1[i], EXPRmode);
+ Type tp;
+ if (stats1.length > 0) {
+ stats1[stats1.length - 1] =
+ transform(stats1[stats1.length - 1], lastmode, pt);
+ tp = checkNoEscape(tree.pos, stats1[stats1.length - 1].type);
+ } else {
+ tp = definitions.UNIT_TYPE;
+ }
+ popContext();
+ return copy.Block(tree, stats1)
+ .setType(tp);
+
+ case Visitor(Tree.CaseDef[] cases):
+ return transform(desugarize.Visitor(tree));
+
+ case Assign(Apply(Tree funarray, Tree[] vparam), Tree rhs):
+ return transform(desugarize.Update(tree));
+
+ case Assign(Tree lhs, Tree rhs):
+ Tree lhs1 = transform(lhs, EXPRmode);
+ Symbol varsym = lhs1.symbol();
+ if (varsym != null && (varsym.flags & ACCESSOR) != 0) {
+ return transform(desugarize.Assign(tree.pos, lhs, rhs));
+ } else if (varsym == null || (varsym.flags & MUTABLE) == 0) {
+ return error(tree, "assignment to non-variable");
+ } else {
+ Tree rhs1 = transform(rhs, EXPRmode, lhs1.type);
+ return copy.Assign(tree, lhs1, rhs1)
+ .setType(definitions.UNIT_TYPE);
+ }
+
+ case If(Tree cond, Tree thenp, Tree elsep):
+ Tree cond1 = transform(cond, EXPRmode, definitions.BOOLEAN_TYPE);
+ if (elsep == Tree.Empty) {
+ Tree thenp1 =
+ transform(thenp, EXPRmode, definitions.UNIT_TYPE);
+ Tree elsep1 = make.Block(tree.pos, Tree.EMPTY_ARRAY)
+ .setType(definitions.UNIT_TYPE);
+ return copy.If(tree, cond1, thenp1, elsep1)
+ .setType(definitions.UNIT_TYPE);
+ } else {
+ Tree thenp1 = transform(thenp, EXPRmode, pt);
+ Tree elsep1 = transform(elsep, EXPRmode, pt);
+ return copy.If(tree, cond1, thenp1, elsep1)
+ .setType(Type.lub(new Type[]{thenp1.type, elsep1.type}));
+ }
+
+ case New(Tree.Template templ):
+ switch (templ) {
+ case Template(Tree[] parents, Tree[] body):
+ if (parents.length == 1 && body.length == 0) {
+ Tree parent1 = transform(parents[0], CONSTRmode, pt);
+ Tree.Template templ1 = (Tree.Template)
+ copy.Template(templ, new Tree[]{parent1}, body)
+ .setType(parent1.type).setSymbol(Symbol.NONE);
+ Type owntype = parent1.type;
+ if ((owntype.symbol().constructor().flags &
+ ABSTRACTCLASS) != 0) {
+ error(tree.pos, owntype.symbol() +
+ " is abstract; cannot be instantiated");
+ }
+ return copy.New(tree, templ1)
+ .setType(owntype);
+ } else {
+ pushContext(tree, context.owner, new Scope(context.scope));
+ Tree cd = make.ClassDef(
+ templ.pos,
+ 0,
+ Names.ANON_CLASS_NAME.toTypeName(),
+ Tree.ExtTypeDef.EMPTY_ARRAY,
+ Tree.ExtValDef.EMPTY_ARRAY_ARRAY,
+ Tree.Empty,
+ templ);
+ enterSym(cd);
+ cd = transform(cd);
+ Symbol clazz = cd.symbol();
+ if (clazz.kind != CLASS)
+ return Tree.Bad().setType(Type.ErrorType);
+
+ // compute template's type with new refinement scope.
+ Type[] parentTypes = clazz.info().parents();
+ Scope refinement = new Scope();
+ Type base = Type.compoundType(parentTypes, Scope.EMPTY);
+ Type tp = Type.compoundType(
+ parentTypes, refinement, clazz);
+ Scope.SymbolIterator it = clazz.members().iterator();
+ while (it.hasNext()) {
+ Symbol sym1 = it.next();
+ if (base.lookupNonPrivate(sym1.name).kind != NONE &&
+ !base.memberType(sym1).isSameAs(sym1.type()))
+ refinement.enter(sym1);
+ }
+ if (refinement.elems == Scope.Entry.NONE &&
+ parentTypes.length == 1)
+ tp = parentTypes[0];
+ else
+ tp = checkNoEscape(tree.pos, tp);
+
+ Tree alloc =
+ gen.Typed(
+ gen.New(
+ gen.mkRef(tree.pos,
+ Type.localThisType, clazz.constructor())),
+ tp);
+ popContext();
+ return make.Block(tree.pos, new Tree[]{cd, alloc})
+ .setType(tp);
+ }
+ default:
+ throw new ApplicationError();
+ }
+
+ case Typed(Tree expr, Tree tpe):
+ Tree tpe1 = transform(tpe, TYPEmode);
+ Tree expr1 = transform(expr, EXPRmode, tpe1.type);
+ return copy.Typed(tree, expr1, tpe1)
+ .setType(tpe1.type);
+
+ case Tuple(Tree[] trees):
+ Tree tree1 = transform(desugarize.Tuple(tree), mode, pt);
+ if (trees.length > 0 && (mode & EXPRmode) != 0)
+ tree1 = desugarize.postTuple(tree1);
+ return tree1;
+
+ case Function(Tree.ValDef[] vparams, Tree body):
+ pushContext(tree, context.owner, new Scope(context.scope));
+ Type restype = desugarize.preFunction(vparams, pt);
+ enterParams(vparams);
+ Tree body1 = transform(body, EXPRmode, restype);
+ if (!infer.isFullyDefined(restype)) restype = body1.type;
+ popContext();
+ Tree tree1 = copy.Function(tree, vparams, body1);
+ Tree tree2 = transform(desugarize.Function(tree1, restype));
+ return desugarize.postFunction(tree2);
+
+ case TypeApply(Tree fn, Tree[] args):
+ Tree fn1 = transform(fn, EXPRmode | FUNmode, Type.AnyType);
+ Tree[] args1 = transform(args, TYPEmode);
+ Type[] argtypes = Tree.typeOf(args1);
+
+ // resolve overloading
+ switch (fn1.type) {
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ try {
+ infer.polyAlternative(fn1, alts, alttypes, args.length);
+ } catch (Type.Error ex) {
+ error(tree, ex.msg);
+ }
+ }
+
+ // match against arguments
+ switch (fn1.type) {
+ case PolyType(Symbol[] tparams, Type restp):
+ if (tparams.length == argtypes.length) {
+ int i = 0;
+ while (i < tparams.length &&
+ (context.delayArgs ||
+ argtypes[i].isSubType(
+ tparams[i].info().subst(tparams, argtypes))))
+ i++;
+ if (i == tparams.length) {
+ return copy.TypeApply(tree, fn1, args1)
+ .setType(restp.subst(tparams, argtypes));
+ }
+ }
+ break;
+ case ErrorType:
+ return tree.setType(Type.ErrorType);
+ }
+ return error(tree,
+ infer.toString(fn1.symbol(), fn1.type) +
+ " cannot be applied to " +
+ ArrayApply.toString(argtypes, "[", ",", "]"));
+
+ case Apply(Tree fn, Tree[] args):
+ Tree tree1 = transformApply(tree, fn, args);
+
+ // handle the case of a case method call specially.
+ Symbol fsym = TreeInfo.methSymbol(tree1);
+ if ((mode & (EXPRmode | FUNmode)) == EXPRmode &&
+ fsym != null && (fsym.flags & CASE) != 0) {
+ Symbol constr = fsym.type().resultType().symbol().constructor();
+ Template templ = make.Template(
+ tree1.pos,
+ new Tree[]{desugarize.toConstructor(tree1, constr)},
+ Tree.EMPTY_ARRAY);
+ templ.setSymbol(Symbol.NONE).setType(tree1.type);
+ return adapt(
+ make.New(tree1.pos, templ).setType(tree1.type), mode, pt);
+ } else {
+ return tree1;
+ }
+
+ case Super(Tree tpe):
+ Symbol enclClazz = context.enclClass.owner;
+ if (enclClazz != null) {
+ // we are in a class or module
+ Tree tpe1 = transform(tpe, TYPEmode); // ignored for now.
+ switch (enclClazz.info()) {
+ case CompoundType(Type[] parents, _):
+ return copy.Super(tree, tpe1)
+ .setType(Type.compoundType(parents, Scope.EMPTY));
+ case ErrorType:
+ return tree.setType(Type.ErrorType);
+ default:
+ throw new ApplicationError();
+ }
+ } else {
+ return error(tree,
+ "super can be used only in a class, module, or template");
+ }
+
+ case This(Tree qual):
+ if (qual == Tree.Empty) {
+ Symbol clazz = context.enclClass.owner;
+ if (clazz != null) { // we are in a class or module
+ return make.This(
+ tree.pos,
+ make.Ident(tree.pos, clazz.name)
+ .setSymbol(clazz).setType(clazz.type()))
+ .setType(clazz.thisType());
+ } else {
+ return error(
+ tree, tree +
+ " can be used only in a class, module, or template");
+ }
+ } else {
+ Tree qual1 = transform(qual, TYPEmode | FUNmode);
+ Symbol clazz = qual1.symbol();
+ if (clazz.kind == CLASS) {
+ Context clazzContext = context.outerContext(clazz);
+ if (clazzContext != Context.NONE) {
+ return tree.setType(clazz.thisType());
+ } else {
+ return error(
+ qual, clazz.name + " is not an enclosing class");
+ }
+ } else {
+ return error(qual, "class identifier expected");
+ }
+ }
+
+ case Select(Tree qual, Name name):
+ Tree qual1 = transform(qual, EXPRmode | POLYmode | QUALmode);
+ if (name.isTypeName()) qual1 = checkStable(qual1);
+ return transformSelect(
+ tree,
+ adapt(qual1, EXPRmode | POLYmode | QUALmode, Type.AnyType),
+ name);
+
+ case Ident(Name name):
+ if (mode == PATTERNmode && name.isVariable()) {
+ //System.out.println("pat var " + name + ":" + pt);//DEBUG
+ Symbol vble = new TermSymbol(
+ tree.pos, name, context.owner, 0).setType(pt);
+ if (name != Names.WILDCARD) enterInScope(vble);
+ return tree.setSymbol(vble).setType(pt);
+ } else {
+ return transformIdent(tree, name);
+ }
+
+ case Literal(Object value):
+ return tree.setType(definitions.getType(value2TypeName(value)));
+
+ case SingletonType(Tree ref):
+ Tree ref1 = transform(ref, EXPRmode, Type.AnyType);
+ return copy.SingletonType(tree, ref1)
+ .setType(checkObjectType(tree.pos, ref1.type));
+
+ case SelectFromType(Tree qual, Name name):
+ Tree qual1 = transform(qual, TYPEmode);
+ return transformSelect(tree, qual1, name);
+
+ case CompoundType(Tree[] parents, Tree[] refinements):
+ Tree[] parents1 = transform(parents, TYPEmode);
+ Type[] ptypes = Tree.typeOf(parents);
+ Scope members = new Scope();
+ Type self = Type.compoundType(ptypes, members);
+ Symbol clazz = self.symbol();
+ validateBaseTypes(clazz);
+ pushContext(tree, clazz, members);
+ for (int i = 0; i < refinements.length; i++) {
+ enterSym(refinements[i]).flags |= OVERRIDE;
+ }
+ Tree[] refinements1 = transformStatSeq(refinements, Symbol.NONE);
+ checkAllOverrides(clazz);
+ popContext();
+ return copy.CompoundType(tree, parents1, refinements1)
+ .setType(self);
+
+ case AppliedType(Tree tpe, Tree[] args):
+ Tree tpe1 = transform(tpe, TYPEmode | FUNmode);
+ Tree[] args1 = transform(args, TYPEmode);
+ Type[] argtypes = Tree.typeOf(args);
+ Symbol clazz = tpe1.type.unalias().symbol();
+ Symbol[] tparams = clazz.typeParams();
+ if (tpe1.type != Type.ErrorType) {
+ if (tparams.length != args.length) {
+ if (tparams.length == 0)
+ return error(tree, tpe1.type +
+ " does not take type parameters");
+ else
+ return error(tree,
+ "wrong number of type arguments for " +
+ tpe1.type);
+ } else {
+ try {
+ if (!context.delayArgs)
+ infer.checkBounds(tparams, argtypes, "");
+ } catch (Type.Error ex) {
+ return error(tree, ex.msg);
+ }
+ }
+ return copy.AppliedType(tree, tpe1, args1)
+ .setType(Type.appliedType(tpe1.type, argtypes));
+ } else {
+ return tpe1;
+ }
+
+ case CovariantType(Tree tpe):
+ Tree tpe1 = transform(tpe, TYPEmode);
+ return copy.CovariantType(tree, tpe1)
+ .setType(Type.covarType(tpe1.type));
+
+ case FunType(_, _):
+ return transform(desugarize.FunType(tree));
+
+ case TupleType(Tree[] types):
+ Tree tree1 = desugarize.mkTupleType(tree.pos, types);
+ return transform(desugarize.mkTupleType(tree.pos, types));
+
+ default:
+ throw new ApplicationError("illegal tree: " + tree);
+ }
+ } catch (Type.Error ex) {
+ if (ex instanceof CyclicReference) {
+ if (global.debug) ex.printStackTrace();//DEBUG
+ CyclicReference cyc = (CyclicReference) ex;
+ if (cyc.info instanceof LazyTreeType) {
+ switch (((LazyTreeType) cyc.info).tree) {
+ case ValDef(_, _, _, _):
+ return error(tree, "recursive " + cyc.sym + " needs type");
+ case DefDef(_, _, _, _, _, _):
+ return error(tree, "recursive " + cyc.sym + " needs result type");
+ }
+ }
+ }
+ return error(tree, ex.msg);
+ }
+ }
+
+// Contexts -------------------------------------------------------------------
+
+ /** Push new context associated with given tree, owner, and scope on stack.
+ * Fields `imports' and, possibly, `enclClass' are inherited from parent.
+ */
+ void pushContext(Tree tree, Symbol owner, Scope scope) {
+ context = new Context(tree, owner, scope, context);
+ }
+
+ /** Pop context from stack.
+ */
+ void popContext() {
+ context = context.outer;
+ }
+
+// Lazy Types ------------------------------------------------------------------
+
+ /** A lazy type which, when forced returns the type of a symbol defined
+ * in `tree'.
+ */
+ class LazyTreeType extends Type.LazyType {
+ Tree tree;
+ Unit u;
+ Infer i;
+ Context c;
+
+ LazyTreeType(Tree tree) {
+ this.tree = tree;
+ this.u = unit;
+ this.i = infer;
+ this.c = context;
+ }
+ public void complete(Symbol sym) {
+ //if (sym.isConstructor()) sym.constructorClass().initialize();
+ //else if (sym.isModule()) sym.moduleClass().initialize();
+ defineSym(tree, u, i, c);
+ }
+ }
+
+ /** A lazy type for case constructor methods (whose name is a term name)
+ * which sets the method's type to the class constructor type.
+ */
+ class LazyConstrMethodType extends LazyTreeType {
+ LazyConstrMethodType(Tree tree) {
+ super(tree);
+ }
+ public void complete(Symbol sym) {
+ sym.setInfo(tree.symbol().constructor().type());
+ }
+ }
+}
+
diff --git a/sources/scalac/typechecker/AnalyzerPhase.java b/sources/scalac/typechecker/AnalyzerPhase.java
new file mode 100644
index 0000000000..9542da51e3
--- /dev/null
+++ b/sources/scalac/typechecker/AnalyzerPhase.java
@@ -0,0 +1,103 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+package scalac.typechecker;
+
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import scalac.checkers.*;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+public class AnalyzerPhase extends PhaseDescriptor {
+
+ /* final */ Context startContext;
+ HashMap/*<Unit,Context>*/ contexts = new HashMap();
+ ArrayList/*<Unit>*/ newSources = new ArrayList();
+
+ public void initialize(Global global, int id) {
+ super.initialize(global, id);
+ Definitions definitions = global.definitions;
+ this.startContext = new Context(
+ Tree.Empty,
+ definitions.ROOT_CLASS,
+ definitions.ROOT_CLASS.members(),
+ Context.NONE);
+ this.startContext.enclClass = this.startContext;
+
+ if (!global.noimports) {
+ TreeFactory make = global.make;
+
+ Tree java = make.Ident(Position.NOPOS, Names.java)
+ .setSymbol(definitions.JAVA)
+ .setType(Type.singleType(definitions.ROOT_TYPE, definitions.JAVA));
+ Tree javalang = make.Select(Position.NOPOS, java, Names.lang)
+ .setSymbol(definitions.JAVALANG)
+ .setType(Type.singleType(java.type, definitions.JAVALANG));
+ Tree importjavalang = make.Import(
+ Position.NOPOS, javalang, new Name[]{Names.WILDCARD})
+ .setSymbol(definitions.JAVALANG)
+ .setType(definitions.UNIT_TYPE);
+ startContext.imports = new ImportList(
+ importjavalang, startContext.scope, startContext.imports);
+
+ Tree scala = make.Ident(Position.NOPOS, Names.scala)
+ .setSymbol(definitions.SCALA)
+ .setType(Type.singleType(definitions.ROOT_TYPE, definitions.SCALA));
+ Tree importscala = make.Import(
+ Position.NOPOS, scala, new Name[]{Names.WILDCARD})
+ .setSymbol(definitions.SCALA)
+ .setType(definitions.UNIT_TYPE);
+ startContext.imports = new ImportList(
+ importscala, new Scope(), startContext.imports);
+
+ scala = make.Ident(Position.NOPOS, Names.scala)
+ .setSymbol(definitions.SCALA)
+ .setType(scala.type);
+ Symbol scalaPredefSym = definitions.getModule(Names.scala_Predef);
+ Tree scalaPredef = make.Select(Position.NOPOS, scala, Names.Predef)
+ .setSymbol(scalaPredefSym)
+ .setType(Type.singleType(scala.type, scalaPredefSym));
+
+ Tree importscalaPredef = make.Import(
+ Position.NOPOS, scalaPredef, new Name[]{Names.WILDCARD})
+ .setSymbol(scalaPredefSym)
+ .setType(definitions.UNIT_TYPE);
+ startContext.imports = new ImportList(
+ importscalaPredef, new Scope(), startContext.imports);
+ }
+ }
+
+ public String name() {
+ return "analyze";
+ }
+
+ public String description () {
+ return "name and type analysis";
+ }
+
+ public String taskDescription() {
+ return "type checking";
+ }
+
+ public Phase createPhase(Global global) {
+ return new Analyzer(global, this);
+ }
+
+ public Checker[] postCheckers(Global global) {
+ return new Checker[] {
+ /* todo: uncomment
+ new CheckSymbols(global),
+ new CheckTypes(global),
+ new CheckOwners(global)
+ */
+ };
+ }
+}
diff --git a/sources/scalac/typechecker/Context.java b/sources/scalac/typechecker/Context.java
new file mode 100644
index 0000000000..3f1b30388f
--- /dev/null
+++ b/sources/scalac/typechecker/Context.java
@@ -0,0 +1,63 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+package scalac.typechecker;
+
+import scalac.symtab.*;
+import scalac.ast.Tree;
+
+public class Context {
+ Tree tree; // Tree associated with this context
+ Symbol owner; // The current owner
+ Scope scope; // The current scope
+ ImportList imports; // The current import list
+ Context outer; // The next outer context
+ Context enclClass = this; // The next outer context whose tree
+ // is a class template
+ boolean delayArgs = false; // delay checking of type arguments
+
+ public Context() {}
+
+ public Context(Tree tree, Context outer) {
+ this(tree, outer.owner, outer.scope, outer);
+ }
+
+ public Context(Tree tree, Symbol owner, Scope scope, Context outer) {
+ this.tree = tree;
+ this.owner = owner;
+ this.scope = scope;
+ this.imports = outer.imports;
+ if (tree instanceof Tree.Template ||
+ tree instanceof Tree.CompoundType) this.enclClass = this;
+ else this.enclClass = outer.enclClass;
+ this.delayArgs = outer.delayArgs;
+ this.outer = outer;
+ }
+
+ public static Context NONE = new Context();
+
+ Context outerContext(Symbol clazz) {
+ Context c = this;
+ while (c != Context.NONE && c.owner != clazz) c = c.outer;
+ return c;
+ }
+
+ boolean isTopLevel() {
+ switch (tree) {
+ case Block(_):
+ return false;
+ case Template(_, _):
+ return outer.tree instanceof Tree.PackageDef;
+ case Empty:
+ return true;
+ default:
+ return outer.isTopLevel();
+ }
+ }
+}
+
diff --git a/sources/scalac/typechecker/DeSugarize.java b/sources/scalac/typechecker/DeSugarize.java
new file mode 100644
index 0000000000..fda3d0f492
--- /dev/null
+++ b/sources/scalac/typechecker/DeSugarize.java
@@ -0,0 +1,681 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.typechecker;
+
+import java.io.*;
+import java.util.*;
+import scalac.*;
+import scalac.util.*;
+import scalac.symtab.*;
+import scalac.ast.*;
+import scalac.ast.printer.*;
+import Tree.*;
+
+/** A transformer for removing syntactic sugar. This transformer does
+ * not need any type or symbol-table information.
+ *
+ * @author Christine Roeckl, Martin Odersky
+ * @version 2.0
+ */
+public class DeSugarize implements Kinds, Modifiers {
+
+ /** the global environment
+ */
+ protected Global global;
+
+ /** the tree factory
+ */
+ protected TreeFactory make;
+
+ /** The copying factory
+ */
+ protected TreeCopyFactory copy;
+
+ /** the tree generator
+ */
+ protected TreeGen gen;
+
+ /** the type inferencer
+ */
+ protected Infer infer;
+
+ /** the name creator
+ */
+ protected final FreshNameCreator freshNameCreator;
+
+ /** the constructor
+ */
+ public DeSugarize(Analyzer analyzer, Global global) {
+ this.global = global;
+ this.make = analyzer.make;
+ this.copy = analyzer.copy;
+ this.gen = analyzer.gen;
+ this.infer = analyzer.infer;
+ this.freshNameCreator = global.freshNameCreator;
+ }
+
+
+// Auxiliary definitions and functions -------------------------------------------
+
+ /** introduce fresh variable of the form "deS$56"
+ */
+ Name getvar() {
+ return freshNameCreator.newName("ds", '$');
+ }
+
+ Name setterName(Name name) {
+ return name.append(Names._EQ);
+ }
+
+ Name parameterName(int i) {
+ return Name.fromString("x$" + i);
+ }
+
+ Name tupleSelectorName(int i) {
+ return Name.fromString("_" + i);
+ }
+
+ /** extract variables from a pattern
+ */
+ void getVariables(Tree tree, ArrayList vars) {
+ switch(tree) {
+ case Ident(Name name):
+ if (name.isVariable()) vars.add(name);
+ break;
+ case Typed(Tree expr, Tree type):
+ getVariables(expr, vars);
+ break;
+ case Apply(Tree fn, Tree[] args):
+ switch (fn) {
+ case Apply(_, _): getVariables(fn, vars);
+ }
+ for (int i = 0; i < args.length; i++)
+ getVariables(args[i], vars);
+ break;
+ case Tuple(Tree[] elems):
+ for (int i = 0; i < elems.length; i++)
+ getVariables(elems[i], vars);
+ break;
+ default:
+ throw new ApplicationError ("illegal pattern", tree);
+ }
+ }
+
+// Transform functions -----------------------------------------------------
+
+ /** [T_1, ..., T_N] => scala.TupleN[+T_1, ..., +T_N]
+ */
+ public Tree mkTupleType(int pos, Tree[] types) {
+ assert types.length > 0;
+ Tree[] types1 = new Tree[types.length];
+ for (int i = 0; i < types.length; i++)
+ types1[i] = make.CovariantType(pos, types[i]);
+ return make.AppliedType(pos,
+ gen.mkTycon(pos,
+ global.definitions.getType(
+ Name.fromString("scala.Tuple" + types.length))),
+ types1);
+ }
+
+ /** (T_1, ..., T_N) => T ==> scala.FunctionN[T_1, ..., T_N, +T]
+ */
+ public Tree FunType(Tree tree) {
+ switch(tree) {
+ case FunType(Tree[] argtpes, Tree restpe):
+ Tree[] types = new Tree[argtpes.length + 1];
+ System.arraycopy(argtpes, 0, types, 0, argtpes.length);
+ types[argtpes.length] = make.CovariantType(restpe.pos, restpe);
+ return make.AppliedType(tree.pos,
+ make.Select(tree.pos,
+ make.Ident(tree.pos, Names.scala),
+ Name.fromString("Function" + argtpes.length).toTypeName()),
+ types);
+ default:
+ throw new ApplicationError("function type expected", tree);
+ }
+ }
+
+ /** [] ==> scala.Nil()
+ * [e_1,...,e_N] ==> scala.TupleN(e_1,...,e_n) (N >= 1)
+ * mode is either EXPRmode or PATmode
+ */
+ public Tree Tuple(Tree tree) {
+ switch(tree) {
+ case Tuple(Tree[] trees):
+ Name n = trees.length == 0 ? Names.Nil
+ : Name.fromString("Tuple" + trees.length);
+ Tree select = make.Select(tree.pos,
+ make.Ident(tree.pos, Names.scala), n.toConstrName());
+ return make.Apply(tree.pos, select, trees);
+
+ default:
+ throw new ApplicationError("tuple expected", tree);
+ }
+ }
+
+ /** constr call C of type TupleN[T_1,...,T_N] ==> (C: TupleN[+T_1,...,+T_N])
+ */
+ Tree postTuple(Tree tree) {
+ Type[] targs = tree.type.typeArgs();
+ if (targs.length == 0) return tree;
+ else return gen.Typed(tree, global.definitions.tupleType(targs));
+ }
+
+ /** Convert method to function type.
+ */
+ Type meth2fun(Type tp) {
+ switch (tp) {
+ case MethodType(Symbol[] params, Type restype):
+ return global.definitions.functionType(
+ Symbol.type(params), meth2fun(restype));
+ default:
+ return tp;
+ }
+ }
+
+ /** If `pt' is a matching function type insert missing parameters
+ * in `vparams' from it, and return result type,
+ * else return AnyType.
+ */
+ public Type preFunction(ValDef[] vparams, Type pt) {
+ switch (pt) {
+ case TypeRef(Type pre, Symbol psym, Type[] ptargs):
+ if (psym.fullName().startsWith(Names.scala_Function) &&
+ ptargs.length == vparams.length + 1) {
+ for (int i = 0; i < vparams.length; i++)
+ assignType(vparams[i], ptargs[i]);
+ return ptargs[vparams.length].dropVariance();
+ }
+ }
+ return Type.AnyType;
+ }
+ //where
+ void assignType(ValDef vparam, Type pt) {
+ if (vparam.tpe == Tree.Empty && infer.isFullyDefined(pt))
+ vparam.tpe = gen.mkType(vparam.pos, pt);
+ }
+
+ /** (x_1: T_1, ..., x_n: T_N) => e ==>
+ * new scala.Function[T_1, ..., T_N, T] with {
+ * def apply(x_1: T_1, ..., x_N: T_N): T = e
+ * }
+ * where T = `restpe'
+ * T_i = `argtpes[i]'
+ * T_i's might be missing in the original tree.
+ */
+ public Tree Function(Tree tree, Type restype) {
+ assert !restype.isCovarType();
+ switch (tree) {
+ case Function(ValDef[] vparams, Tree body):
+ int length = vparams.length;
+ Tree restpe = gen.mkType(tree.pos, meth2fun(restype));
+
+ Tree[] argtpes = new Tree[length + 1];
+ for (int i = 0; i < length; i++)
+ argtpes[i] = vparams[i].tpe;
+ argtpes[vparams.length] = restpe;
+ Tree constr = make.TypeApply(tree.pos,
+ make.Select(tree.pos,
+ make.Ident(tree.pos, Names.scala),
+ Name.fromString("Function" + length).toConstrName()),
+ argtpes);
+
+ Tree applyDef = make.DefDef(
+ tree.pos, 0, Names.apply,
+ Tree.ExtTypeDef.EMPTY_ARRAY, new ValDef[][]{vparams},
+ restpe, body);
+
+ Tree result = make.New(tree.pos,
+ make.Template(tree.pos, new Tree[]{constr}, new Tree[]{applyDef}));
+ print(tree, "mkfun", result);
+ return result;
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** e of type FunctionN[T_1,...,T_N, T] -->
+ * (e: FunctionN[T_1,...,T_n, +T])
+ */
+ Tree postFunction(Tree tree) {
+ Type[] targs = tree.type.typeArgs();
+ if (targs.length >= 1) {
+ Type[] targs1 = new Type[targs.length - 1];
+ System.arraycopy(targs, 0, targs1, 0, targs1.length);
+ Tree result = gen.Typed(
+ tree,
+ global.definitions.functionType(targs1, targs[targs1.length]));
+ print(tree, "postfun", result);
+ return result;
+ } else return tree;
+ }
+
+ /** match => this.match
+ * match[targs] => this.match[targs]
+ * tree is already attributed and attributes need to be preserved.
+ */
+ Tree postMatch(Tree tree, Symbol currentclazz) {
+ switch (tree) {
+ case Ident(Name name):
+ return
+ make.Select(tree.pos,
+ make.This(tree.pos, Tree.Empty).setType(currentclazz.type()),
+ name).setSymbol(tree.symbol()).setType(tree.type);
+ case TypeApply(Tree fn, Tree[] args):
+ return copy.TypeApply(tree, postMatch(fn, currentclazz), args);
+ default:
+ return tree;
+ }
+ }
+
+ /** { cases } ==> (x => x.match {cases})
+ * only called when match has to be added
+ * no type for parameter x
+ */
+ public Tree Visitor(Tree tree) {
+ switch(tree) {
+ case Visitor(CaseDef[] cases):
+ Name x = getvar();
+ ValDef param = (ValDef) make.ValDef(
+ tree.pos, 0, x, Tree.Empty, Tree.Empty);
+ Tree xuse = make.Ident(tree.pos, x);
+ // x.match {cases}
+ Tree body = make.Apply(tree.pos,
+ make.Select(tree.pos, xuse, Names.match),
+ new Tree[]{tree});
+ return make.Function(tree.pos, new ValDef[]{param}, body);
+ default:
+ throw new ApplicationError("visitor expected", tree);
+ }
+ }
+
+ /** e = e' ==> e_=(e')
+ */
+ public Tree Assign(int pos, Tree lhs, Tree rhs) {
+ Tree lhs1;
+ switch (lhs) {
+ case Ident(Name name):
+ lhs1 = make.Ident(lhs.pos, setterName(name));
+ break;
+ case Select(Tree qual, Name name):
+ lhs1 = make.Select(lhs.pos, qual, setterName(name));
+ break;
+ default:
+ throw new ApplicationError();
+ }
+ return make.Apply(pos, lhs1, new Tree[]{rhs});
+ }
+
+ /** e(args) = e' ==> e.update(args ; e')
+ */
+ public Tree Update(Tree tree) {
+ switch(tree) {
+ case Assign(Apply(Tree fn, Tree[] args), Tree rhs):
+ // e.update
+ Tree update = make.Select(fn.pos, fn, Names.update);
+ Tree[] args1 = new Tree[args.length + 1];
+ System.arraycopy(args, 0, args1, 0, args.length);
+ args1[args.length] = rhs;
+ return make.Apply(tree.pos, update, args1);
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** expand pattern definitions and variable definitions in templates.
+ */
+ public Tree[] Statements(Tree[] stats, boolean isLocal) {
+ boolean change = false;
+ for (int i = 0; i < stats.length && !change; i++) {
+ switch (stats[i]) {
+ case PatDef(_, _, _):
+ change = true;
+ break;
+ case ValDef(int mods, _, _, _):
+ change = !isLocal && (mods & MUTABLE) != 0;
+ }
+ }
+ if (change) {
+ TreeList ts = new TreeList();
+ for (int i = 0; i < stats.length; i++) {
+ switch (stats[i]) {
+ case PatDef(_, _, _):
+ ts.append(this.PatDef(stats[i]));
+ break;
+ case ValDef(int mods, _, _, _):
+ if (!isLocal && (mods & MUTABLE) != 0)
+ ts.append(this.VarDef(stats[i]));
+ else
+ ts.append(stats[i]);
+ break;
+ default:
+ ts.append(stats[i]);
+ }
+ }
+ stats = ts.toArray();
+ //TextTreePrinter p = new TextTreePrinter();//debug
+ //p.print("desugarized:");//debug
+ //for (int i = 0; i < stats.length; i++) p.print(stats[i]).println();//debug
+ //p.end();//debug
+ return stats;
+ } else {
+ return stats;
+ }
+ }
+
+ /** expands pattern definitions
+ * in case pattern is a simple (typed) identifier:
+ * val x = e ==> val x = e
+ * val x: T = e ==> val x: T = e
+ *
+ * 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
+ *
+ */
+ public Tree[] PatDef(Tree tree) {
+ switch(tree) {
+
+ case PatDef(int mods, Ident(Name name), Tree rhs):
+ // val x = e ==> val x = e
+ return new Tree[]{
+ make.ValDef(tree.pos, mods, name, Tree.Empty, rhs)};
+
+ case PatDef(int mods, Typed(Ident(Name name), Tree type), Tree rhs):
+ // val x: T = e ==> val x: T = e
+ return new Tree[]{
+ make.ValDef(tree.pos, mods, name, type, rhs)};
+
+ case PatDef(int mods, Tree pat, Tree rhs):
+ int pos = tree.pos;
+ ArrayList varlist = new ArrayList();
+ getVariables(pat, varlist);
+ Name[] vars = new Name[varlist.size()];
+ varlist.toArray(vars);
+
+ // (x_1, ..., x_N)
+ Tree[] vtree = new Tree[vars.length];
+ for (int i = 0; i < vars.length; i++) {
+ vtree[i] = make.Ident(pos, vars[i]);
+ }
+ Tree tuple = this.Tuple(make.Tuple(tree.pos, vtree));
+
+ // e.match (case p => (x_1, ..., x_N))
+ CaseDef[] cases = {make.CaseDef(pos, pat, Tree.Empty, tuple)};
+ Tree match = make.Apply(pos,
+ make.Select(pos, rhs, Names.match),
+ new Tree[]{make.Visitor(pos, cases)});
+
+ if (vars.length == 0) {
+ // e.match (case p => ())
+ return new Tree[]{match};
+ } else if (vars.length == 1) {
+ // val x_1 = e.match (case p => (x_1))
+ return new Tree[]{
+ make.ValDef(pos, mods, vars[0], Tree.Empty, match)};
+ } else {
+ // t$
+ Name var = getvar();
+
+ // private synthetic val t$ = e.match (case p => (x_1, ..., x_N))
+ Tree[] res = new Tree[vars.length + 1];
+ res[0] = make.ValDef(pos, PRIVATE | SYNTHETIC, var,
+ Tree.Empty, match);
+ for (int i = 0; i < vars.length; i ++) {
+ // val x_i = t$._i
+ res[i + 1] = make.ValDef(
+ pos, mods, vars[i], Tree.Empty,
+ make.Select(pos, make.Ident(pos, var), tupleSelectorName(i + 1)));
+ }
+ print(pat, " -> ", new Block(res));//debug
+ return res;
+ }
+ default:
+ throw new ApplicationError("pattern definition expected", tree);
+ }
+ }
+
+ public Tree[] VarDef(Tree tree) {
+ switch (tree) {
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ Name varname = Name.fromString(name + "$");
+ Tree vardef1 = copy.ValDef(
+ tree, PRIVATE | MUTABLE | SYNTHETIC, varname, tpe, rhs);
+ Tree getter = make.DefDef(
+ tree.pos, mods | ACCESSOR, name,
+ Tree.ExtTypeDef.EMPTY_ARRAY,
+ Tree.ExtValDef.EMPTY_ARRAY_ARRAY,
+ tpe,
+ (rhs == Tree.Empty) ? Tree.Empty : make.Ident(tree.pos, varname));
+ Tree setter = make.DefDef(
+ tree.pos, mods | ACCESSOR, setterName(name),
+ Tree.ExtTypeDef.EMPTY_ARRAY,
+ new ValDef[][]{{
+ (ValDef) make.ValDef(
+ tree.pos, SYNTHETIC, parameterName(0), tpe, Tree.Empty)}},
+ gen.mkType(tree.pos, global.definitions.UNIT_TYPE),
+ (rhs == Tree.Empty) ? Tree.Empty
+ : make.Assign(
+ tree.pos,
+ make.Ident(tree.pos, varname),
+ make.Ident(tree.pos, parameterName(0))));
+ if (rhs == Tree.Empty) return new Tree[]{getter, setter};
+ else return new Tree[]{vardef1, getter, setter};
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** Tree represents an application of a constructor method of a case class
+ * (whose name is a term name). Convert this tree to application of
+ * the case classe's primary constructor `constr'.
+ */
+ public Tree toConstructor(Tree tree, Symbol constr) {
+ switch (tree) {
+ case Apply(Tree fn, Tree[] args):
+ return copy.Apply(tree, toConstructor(fn, constr), args);
+ case TypeApply(Tree fn, Tree[] args):
+ return copy.TypeApply(tree, toConstructor(fn, constr), args);
+ case Ident(Name name):
+ return copy.Ident(tree, constr.name).setSymbol(constr);
+ case Select(Tree qual, Name name):
+ return copy.Select(tree, qual, constr.name).setSymbol(constr);
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** 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
+ */
+ public Tree etaExpand(Tree tree, Type type) {
+ TreeList defs = new TreeList();
+ Tree lambda =
+ toFunction(toApply(liftoutPrefix(tree, defs), type), type);
+ defs.append(lambda);
+ Tree result = make.Block(tree.pos, defs.toArray());
+ print(tree, "eta", result);//debug
+ return result;
+ }
+
+ private static String preName = "eta$";
+
+ /** Append to `defs' value definitions for all non-stable subexpressions
+ * of the function application `tree'
+ */
+ public Tree liftoutPrefix(Tree tree, TreeList defs) {
+ switch (tree) {
+ case Ident(_):
+ return tree;
+
+ case Select(Tree qual, Name name):
+ return copy.Select(tree, liftout(qual, defs), name);
+
+ case TypeApply(Tree fn, Tree[] args):
+ return copy.TypeApply(tree, liftoutPrefix(fn, defs), args);
+
+ case Apply(Tree fn, Tree[] args):
+ return copy.Apply(tree, liftoutPrefix(fn, defs), liftout(args, defs));
+
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ public Tree[] liftout(Tree[] trees, TreeList defs) {
+ Tree[] trees1 = trees;
+ for (int i = 0; i < trees.length; i++) {
+ Tree tree = trees[i];
+ Tree tree1 = liftout(tree, defs);
+ if (tree1 != tree && trees1 == trees) {
+ trees1 = new Tree[trees.length];
+ System.arraycopy(trees, 0, trees1, 0, trees.length);
+ }
+ trees1[i] = tree1;
+ }
+ return trees1;
+ }
+
+ public Tree liftout(Tree tree, TreeList defs) {
+ if (!TreeInfo.isPureExpr(tree)) {
+ Name vname = Name.fromString(preName + defs.length());
+ defs.append(
+ make.ValDef(
+ tree.pos, SYNTHETIC, vname, Tree.Empty, tree));
+ return make.Ident(tree.pos, vname);
+ } else {
+ return tree;
+ }
+ }
+
+ /** f, (syms_1)...(syms_n)T ==> f(ps_1)...(ps_n)
+ */
+ Tree toApply(Tree tree, Type type) {
+ switch(type) {
+ case MethodType(Symbol[] vparams, Type restpe):
+ Tree res = make.Apply(tree.pos, tree, toIdents(vparams));
+ return toApply(res, restpe);
+ default:
+ return tree;
+ }
+ }
+
+ /** e, (syms_1)...(syms_n)T ==> (ps_1 => ... => ps_n => e)
+ */
+ Tree toFunction(Tree tree, Type type) {
+ switch(type) {
+ case MethodType(Symbol[] vparams, Type restpe):
+ return this.Function(
+ make.Function(tree.pos, toVparams(vparams), toFunction(tree, restpe)),
+ restpe);
+ default:
+ return tree;
+ }
+ }
+
+ /** Extract value parameters from type.
+ */
+ ValDef[] toVparams(Symbol[] symbols) {
+ ValDef[] vpars = new ValDef[symbols.length];
+ for (int i = 0; i < symbols.length; i++) {
+ vpars[i] = (ValDef)make.ValDef(
+ symbols[i].pos, 0, symbols[i].name,
+ gen.mkType(symbols[i].pos, symbols[i].type()),
+ Tree.Empty);
+ }
+ return vpars;
+ }
+
+ /** Extract value identifiers from method type.
+ * It is assumed that all symbols are term symbols ==> make.Ident().
+ */
+ Tree[] toIdents(Symbol[] symbols) {
+ Tree[] idents = new Ident[symbols.length];
+ for (int i = 0; i < symbols.length; i++) {
+ idents[i] = make.Ident(symbols[i].pos, symbols[i].name);
+ }
+ return idents;
+ }
+
+ /** Build value element definitions and return its name.
+ */
+ void addCaseElement(TreeList ts, ValDef vparam) {
+ //System.out.println("add case for " + vparam.name);//DEBUG
+ Name name = vparam.name;
+ vparam.name = Name.fromString(name + "$");
+ ts.append(
+ make.ValDef(
+ vparam.pos, CASE, name, vparam.tpe,
+ make.Ident(vparam.pos, vparam.name)));
+ }
+
+ /** add case constructor, value defintiions and access functions.
+ */
+ Tree[] addCaseElements(Tree[] body, ValDef[] vparams) {
+ TreeList stats = new TreeList();
+ for (int i = 0; i < vparams.length; i++) {
+ addCaseElement(stats, vparams[i]);
+ }
+ stats.append(body);
+ return stats.toArray();
+ }
+
+ /** Does list of types inherit from class scala.Algebraic?
+ */
+ boolean inheritsAlgebraic(Type[] tps) {
+ Type algebraic = global.definitions.getType(Names.scala_Algebraic);
+ for (int i = 0; i < tps.length; i++) {
+ if (tps[i].isSubType(algebraic)) return true;
+ }
+ return false;
+ }
+
+ /** Add toString, hashCode and == if class inherts from scala.Algebraic.
+ */
+ Tree[] addCaseMethods(Tree[] body, Symbol clazz, Type[] parents) {
+ if (inheritsAlgebraic(parents)) {
+ /* todo uncomment and implement
+ TreeList stats = new TreeList(body);
+ Symbol[] params = clazz.constructor().firstParams();
+ stats.append(toStringMethod(clazz, params));
+ stats.append(hashcodeMethod(clazz, params));
+ stats.append(equalsMethod(clazz, params));
+ */
+ }
+ return body;
+ }
+
+ //debug
+ void print(Tree tree, String conv, Tree result) {
+ if (global.log()) {
+ new TextTreePrinter()
+ .print(tree).println()
+ .print(" --" + conv + "--> ").println()
+ .print(result).println().end();
+ }
+ }
+}
diff --git a/sources/scalac/typechecker/ImportList.java b/sources/scalac/typechecker/ImportList.java
new file mode 100644
index 0000000000..9e7e79110d
--- /dev/null
+++ b/sources/scalac/typechecker/ImportList.java
@@ -0,0 +1,64 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+package scalac.typechecker;
+
+import scalac.*;
+import scalac.util.*;
+import scalac.symtab.*;
+import scalac.ast.*;
+
+/////////////////////////////////////////////////////////////////////////////
+// Import Lists
+/////////////////////////////////////////////////////////////////////////////
+
+class ImportList {
+ Tree tree; // The import definition
+ Scope enclscope; // The scope in which the import occurs.
+ ImportList prev; // The previous active import list.
+
+ ImportList(Tree tree, Scope enclscope, ImportList prev) {
+ this.tree = tree;
+ this.enclscope = enclscope;
+ this.prev = prev;
+ }
+
+ Tree importPrefix() {
+ switch (tree) {
+ case Import(Tree expr, _): return expr;
+ default: throw new ApplicationError();
+ }
+ }
+
+ Type importType() {
+ return tree.symbol().type();
+ }
+
+ boolean sameImport(ImportList that) {
+ return this.importType().isSameAs(that.importType());
+ }
+
+ Symbol importedSymbol(Name name) {
+ Type t = this.importType();
+ boolean renamed = false;
+ switch (tree) {
+ case Import(Tree expr, Name[] selectors):
+ for (int i = 0; i < selectors.length; i = i + 2) {
+ if (i + 1 < selectors.length && name == selectors[i + 1])
+ return t.lookupNonPrivate(selectors[i]);
+ else if (name == selectors[i])
+ renamed = true;
+ else if (selectors[i] == Names.WILDCARD && !renamed)
+ return t.lookupNonPrivate(name);
+ }
+ return Symbol.NONE;
+ default:
+ throw new ApplicationError();
+ }
+ }
+}
diff --git a/sources/scalac/typechecker/Infer.java b/sources/scalac/typechecker/Infer.java
new file mode 100644
index 0000000000..b9f2071cc0
--- /dev/null
+++ b/sources/scalac/typechecker/Infer.java
@@ -0,0 +1,814 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+package scalac.typechecker;
+
+import scalac.Global;
+import scalac.ApplicationError;
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+
+public class Infer implements Modifiers, Kinds {
+
+ Global global;
+ Definitions definitions;
+ TreeGen gen;
+ TreeFactory make;
+ Substituter substituter;
+
+ public Infer(Transformer trans) {
+ this.global = trans.global;
+ this.definitions = global.definitions;
+ this.gen = trans.gen;
+ this.make = trans.make;
+ this.substituter = new Substituter(global, trans.descr, gen);
+ }
+
+// Error messages -------------------------------------------------------------
+
+ String applyErrorMsg(String msg1, Tree fn,
+ String msg2, Type[] argtypes, Type pt) {
+ return msg1 + toString(fn.symbol(), fn.type) + msg2 +
+ ArrayApply.toString(argtypes, "(", ",", ")") +
+ (pt == Type.AnyType ? "" : " with expected result type " + pt);
+ }
+
+ String typeErrorMsg(String msg, Type found, Type req) {
+ return msg +
+ ";\n found : " + found.toLongString() +
+ "\n required: " + req;
+ }
+
+ String overloadResolveErrorMsg(Symbol sym1, Type tpe1, Symbol sym2, Type tpe2) {
+ return "ambiguous reference to overloaded definition,\n" +
+ "both " + sym1 + ": " + tpe1 + "\n" +
+ "and " + sym2 + ": " + tpe2 + " match.";
+ }
+
+ /** Give a string representation of symbol `sym' with type `tp'
+ * for error diagnostics. `sym' may be null.
+ */
+ static String toString(Symbol sym, Type tp) {
+ return
+ (tp instanceof Type.OverloadedType ? "overloaded " : "") +
+ (sym == null ? "expression" : sym) + " of type " + tp;
+ }
+
+// Helper definitions ---------------------------------------------------------
+
+ /** Is type `tp' a polymorphic method type?
+ */
+ private boolean isPolymorphic(Type tp) {
+ return tp.typeParams().length > 0;
+ }
+
+ /** Is type `tp' a parameterized method type?
+ */
+ /** Is type `tp' a parameterized method type?
+ */
+ boolean isParameterized(Type tp) {
+ switch (tp) {
+ case MethodType(_, _): return true;
+ default: return isPolymorphic(tp);
+ }
+ }
+
+// Tree Substitution -------------------------------------------------------------
+
+ static class Substituter extends Transformer {
+
+ Symbol[] tparams;
+ Type[] targs;
+ TreeGen gen;
+
+ public Substituter(Global global, PhaseDescriptor descr, TreeGen gen) {
+ super(global, descr);
+ this.gen = gen;
+ }
+
+ public Tree apply(Tree tree, Symbol[] tparams, Type[] targs) {
+ this.tparams = tparams;
+ this.targs = targs;
+ return transform(tree);
+ }
+
+ public Tree transform(Tree tree) {
+// System.out.println("[" + ArrayApply.toString(targs,"",",","") + "/" + ArrayApply.toString(tparams,"",",","") + "]" + tree + "@" + tree.symbol());//DEBUG
+ if (tree.type == null) return tree;
+ tree.type = tree.type.subst(tparams, targs);
+ switch (tree) {
+ case Ident(Name name):
+ if (name.isTypeName()) {
+ Symbol sym = tree.symbol();
+ for (int i = 0; i < tparams.length; i++) {
+ if (tparams[i].name == sym.name &&
+ tparams[i].owner() == sym.owner()) {
+ return gen.mkType(tree.pos, targs[i]);
+ }
+ }
+ }
+ return tree;
+ default:
+ return super.transform(tree);
+ }
+ }
+ }
+
+// Type parameter inference -----------------------------------------------------
+
+ private static class NoInstance extends RuntimeException {
+ NoInstance(String msg) {
+ super(msg);
+ }
+ }
+
+ public static class VirtualPolyType extends Type.PolyType {
+ VirtualPolyType(Symbol[] tparams, Type result) {
+ super(tparams, result);
+ }
+ public String toString() {
+ return
+ ArrayApply.toString(Symbol.defString(tparams), "[ ", ",", " ]") +
+ result;
+ }
+ }
+
+ /** map every TypeVar to its constraint.inst field.
+ * throw a NoInstance exception if a NoType or AnyType is encountered.
+ */
+ private static Type.Map instantiateMap = new Type.Map() {
+ public Type apply(Type t) {
+ return instantiate(t);
+ }
+ };
+
+ private static Type instantiate(Type tp) throws NoInstance {
+ switch (tp) {
+ case AnyType:
+ case NoType:
+ throw new NoInstance("undetermined type");
+ case TypeVar(Type origin, Type.Constraint constr):
+ if (constr.inst != Type.NoType) return instantiate(constr.inst);
+ else throw new NoInstance("no unique instantiation of type variable " +
+ origin + " could be found");
+ default:
+ return instantiateMap.map(tp);
+ }
+ }
+
+ /** Map type variable to its instance, or, if `covariant' is true,
+ * to its upper bound (if this is not AnyType);
+ * or return `AnyType' if not possible.
+ */
+ private Type instantiateUpper(Type tp, boolean covariant) throws NoInstance {
+ switch (tp) {
+ case TypeVar(Type origin, Type.Constraint constr):
+ if (constr.inst != Type.NoType) {
+ return instantiate(constr.inst);
+ } else if (covariant && constr.hibounds != Type.List.EMPTY) {
+ maximizeVar(tp);
+ return instantiate(constr.inst);
+ }
+ return Type.AnyType;
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** Is type fully defined, i.e. no embedded anytypes or typevars in it?
+ */
+ public boolean isFullyDefined(Type tp) {
+ try {
+ instantiate(tp);
+ return true;
+ } catch (NoInstance ex) {
+ return false;
+ }
+ }
+
+ /** Do type arguments `targs' conform to formal parameters `tparams'?
+ */
+ private boolean isWithinBounds(Symbol[] tparams, Type[] targs) {
+ // check that covariant types do not appear in F-bounds.
+ for (int i = 0; i < targs.length; i++) {
+ if (targs[i].isCovarType()) {
+ for (int j = 0; j < tparams.length; j++)
+ if (tparams[j].info().contains(tparams[i]))
+ return false;
+ }
+ }
+ for (int i = 0; i < targs.length; i++) {
+ if (!targs[i].dropVariance().isSubType(
+ tparams[i].info().subst(tparams, targs)))
+ return false;
+ }
+ return true;
+ }
+
+ /** throw a type error if arguments not within bounds.
+ */
+ void checkBounds(Symbol[] tparams, Type[] targs, String prefix) {
+ if (!isWithinBounds(tparams, targs)) {
+ throw new Type.Error(
+ prefix + "type arguments " +
+ ArrayApply.toString(targs, "[", ",", "]") + " do not conform to " +
+ tparams[0].owner() + "'s type parameter bounds " +
+ ArrayApply.toString(Symbol.defString(tparams), "[", ",", "]"));
+ }
+ }
+
+ /** Instantiate undetermined variable to its minimal upper bound.
+ * Throw a NoInstance exception if this not possible.
+ */
+ private void maximizeVar(Type tp) throws NoInstance {
+ switch (tp) {
+ case TypeVar(Type origin, Type.Constraint constr):
+ if (constr.inst == Type.NoType) {
+ if (constr.hibounds == Type.List.EMPTY)
+ constr.inst = definitions.ANY_TYPE;
+ else if (constr.hibounds.tail == Type.List.EMPTY)
+ constr.inst = constr.hibounds.head;
+ else {
+ for (Type.List bs = constr.hibounds;
+ bs != Type.List.EMPTY && constr.inst == Type.NoType;
+ bs = bs.tail) {
+ //System.out.println("hibound: " + bs.head);//DEBUG
+ if (isSubSymOfAll(bs.head, constr.hibounds)) {
+ //System.out.println("best: " + bs.head);//DEBUG
+ constr.inst = bs.head.any2typevar();
+ }
+ }
+ }
+ if (constr.inst == Type.NoType ||
+ !isSubTypeOfAll(constr.inst, constr.hibounds)) {
+ throw new NoInstance(
+ "no unique maximal instance exists for type variable " +
+ origin);
+ }
+ }
+ return;
+ default:
+ throw new ApplicationError();
+ }
+ }
+ //where
+ private boolean isSubSymOfAll(Type tp, Type.List tps) {
+ Symbol sym = tp.unalias().symbol();
+ for (Type.List l = tps; l != Type.List.EMPTY; l = l.tail) {
+ if (!isSubSym(sym, l.head.unalias().symbol())) return false;
+ }
+ return true;
+ }
+
+ private boolean isSubSym(Symbol sym, Symbol sym1) {
+ return
+ sym == sym1 ||
+ sym.kind == ERROR ||
+ (sym.kind == TYPE || sym.kind == CLASS) && sym.isSubClass(sym1);
+ }
+
+ private boolean isSubTypeOfAll(Type tp, Type.List tps) {
+ for (Type.List l = tps; l != Type.List.EMPTY; l = l.tail) {
+ if (!tp.isSubType(l.head)) return false;
+ }
+ return true;
+ }
+
+ private void minimizeVar(Type tp) {
+ switch (tp) {
+ case TypeVar(Type origin, Type.Constraint constr):
+ if (constr.inst == Type.NoType && constr.lobounds != Type.List.EMPTY)
+ constr.inst = Type.lub(constr.lobounds.toArray());
+ return;
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ private Type[] freshVars(Symbol[] tparams) {
+ Type[] tvars = new Type[tparams.length];
+ for (int i = 0; i < tvars.length; i++) {
+ tvars[i] = Type.TypeVar(tparams[i].type(), new Type.Constraint());
+ }
+ return tvars;
+ }
+
+ private Type.Map freshInstanceMap = new Type.Map() {
+ public Type apply(Type t) {
+ switch (t) {
+ case PolyType(Symbol[] tparams, Type restp):
+ Symbol[] newparams = new Symbol[tparams.length];
+ for (int i = 0; i < tparams.length; i++)
+ newparams[i] = tparams[i].cloneSymbol();
+ for (int i = 0; i < tparams.length; i++)
+ newparams[i].setInfo(
+ newparams[i].info().subst(tparams, newparams));
+ return Type.PolyType(
+ newparams, apply(restp).subst(tparams, newparams));
+ case OverloadedType(_, _):
+ return map(t);
+ default:
+ return t;
+ }
+ }
+ };
+
+ public Type freshInstance(Type tp) {
+ return freshInstanceMap.apply(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.
+ */
+ private Type normalize(Type tp, Type pt) {
+ switch (tp) {
+ case MethodType(Symbol[] params, Type restype):
+ return global.definitions.functionType(
+ Symbol.type(params), normalize(restype, Type.AnyType));
+ case PolyType(Symbol[] tparams, Type restype):
+ if (tparams.length == 0) return normalize(restype, pt);
+ }
+ return tp;
+ }
+
+ boolean isCompatible(Type tp, Type pt) {
+ return normalize(tp, pt).isSubType(pt);
+ }
+
+ private Symbol[] normalizeArgs(Type[] targs, Symbol[] tparams) {
+ Type.List uninstantiated = Type.List.EMPTY;
+ for (int i = 0; i < targs.length; i++) {
+ if (targs[i] == Type.NoType) {
+ targs[i] = tparams[i].type();
+ uninstantiated = Type.List.append(uninstantiated, targs[i]);
+ }
+ }
+ return Type.symbol(uninstantiated.toArray());
+ }
+
+ /** Return inferred type arguments of polymorphic expression, given
+ * its type parameters and result type and a prototype `pt'.
+ * If no maximal type variables exists that make the
+ * instantiated type a subtype of `pt' and `lastTry' is true, return `null'.
+ */
+ private Type[] instTypeArgs(Symbol[] tparams, Type restype, Type pt) {
+ Type[] tvars = freshVars(tparams);
+ // add all bounds except F-bounds to upper bounds of type variable.
+ for (int i = 0; i < tvars.length; i++) {
+ switch (tvars[i]) {
+ case TypeVar(_, Type.Constraint constr):
+ Type bound = tparams[i].info();
+ if (!bound.containsSome(tparams))
+ constr.hibounds = new Type.List(bound, Type.List.EMPTY);
+ }
+ }
+ Type insttype = restype.subst(tparams, tvars);
+ if (isCompatible(insttype, pt)) {
+ try {
+ Type[] targs = new Type[tvars.length];
+ for (int i = 0; i < tvars.length; i++) {
+ maximizeVar(tvars[i]);
+ targs[i] = instantiate(tvars[i]);
+ }
+ return targs;
+ } catch (NoInstance ex) {
+ }
+ }
+ return null;
+ }
+
+ /** As before, but: don't maximize. Instead map all unistantiated
+ * type vars to AnyType.
+ */
+ public Type[] protoTypeArgs(Symbol[] tparams, Type restype, Type pt,
+ Symbol[] params) {
+ Type[] tvars = freshVars(tparams);
+ Type insttype = restype.subst(tparams, tvars);
+ Type[] targs = new Type[tvars.length];
+ if (isCompatible(insttype, pt)) {
+ for (int i = 0; i < tvars.length; i++) {
+ targs[i] = instantiateUpper(tvars[i], isCovariant(tparams[i], params));
+ }
+ } else {
+ for (int i = 0; i < tvars.length; i++) {
+ targs[i] = Type.AnyType;
+ }
+ }
+ return targs;
+ }
+
+ /** Does given `tparam' occur only covariantly in symbols?
+ */
+ private boolean isCovariant(Symbol tparam, Symbol[] syms) {
+ for (int i = 0; i < syms.length; i++) {
+ if (!isCovariant(tparam, syms[i])) return false;
+ }
+ return true;
+ }
+
+ /** Does given `tparam' occur only covariantly in symbol?
+ */
+ private boolean isCovariant(Symbol tparam, Symbol sym) {
+ switch (sym.kind) {
+ case ERROR: case VAL: case TYPE: return isCovariant(tparam, sym.info());
+ case ALIAS: return !sym.info().contains(tparam);
+ default: return false;
+ }
+ }
+
+ /** Does given `tparam' occur only covariantly in types?
+ */
+ private boolean isCovariant(Symbol tparam, Type[] tps) {
+ for (int i = 0; i < tps.length; i++) {
+ if (!isCovariant(tparam, tps[i])) return false;
+ }
+ return true;
+ }
+
+ /** Does given `tparam' occur only covariantly in argument types?
+ */
+ private boolean isCovariantArgs(Symbol tparam, Type[] tps) {
+ for (int i = 0; i < tps.length; i++) {
+ switch (tps[i]) {
+ case CovarType(Type t):
+ if (!isCovariant(tparam, t)) return false;
+ break;
+ default:
+ if (tps[i].contains(tparam)) return false;
+ }
+ }
+ return true;
+ }
+
+ /** Does given `tparam' occur only covariantly in type?
+ */
+ private boolean isCovariant(Symbol tparam, Type tp) {
+ switch (tp) {
+ case ErrorType:
+ case AnyType:
+ case NoType:
+ case ThisType(Symbol sym):
+ return true;
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ return isCovariant(tparam, pre) && isCovariantArgs(tparam, args);
+ case SingleType(Type pre, Symbol sym):
+ return !pre.contains(tparam);
+ case CompoundType(Type[] parts, Scope members):
+ return isCovariant(tparam, parts) &&
+ isCovariant(tparam, members.elements());
+ default:
+ throw new ApplicationError();
+ }
+ }
+
+ /** Return inferred type arguments, given type parameters, formal parameters and
+ * argument types.
+ * If this is not possible, throw a `NoInstance' exception, or, if
+ * `needToSucceed' is false alternatively return `null'.
+ * Undetermined type arguments are represented by `NoType'.
+ * No check that inferred parameters conform to their bounds is made here.
+ */
+ private Type[] methTypeArgs(Symbol[] tparams, Symbol[] params, Type[] argtypes,
+ boolean needToSucceed) throws NoInstance {
+ //System.out.println("methTypeArgs, tparams = " + ArrayApply.toString(tparams) + ", params = " + ArrayApply.toString(params) + ", type(params) = " + ArrayApply.toString(Symbol.type(params)) + ", argtypes = " + ArrayApply.toString(argtypes));//DEBUG
+
+ Type[] tvars = freshVars(tparams);
+ Type[] formals = Symbol.type(params);
+ if (formals.length != argtypes.length) {
+ if (needToSucceed)
+ throw new NoInstance("parameter lists differ in length");
+ return null;
+ }
+ for (int i = 0; i < formals.length; i++) {
+ if (!isCompatible(argtypes[i].subst(tparams, tvars),
+ formals[i].subst(tparams, tvars))) {
+ if (needToSucceed)
+ throw new NoInstance(
+ typeErrorMsg(
+ "argument expression's type is not compatible with formal parameter type",
+ argtypes[i].subst(tparams, tvars),
+ formals[i].subst(tparams, tvars)));
+ return null;
+ }
+ }
+ Type[] targs = new Type[tvars.length];
+ for (int i = 0; i < tvars.length; i++) {
+ minimizeVar(tvars[i]);
+ targs[i] = (((Type.TypeVar) tvars[i]).constr.inst == Type.NoType)
+ ? Type.NoType
+ : instantiate(tvars[i]);
+ }
+ return targs;
+ }
+
+ /** Create and attribute type application node. Pass arguments for that
+ * `tparams' prefix which is owned by the tree's symbol. If there are remaining
+ * type parameters, substitute corresponding type arguments for them in the
+ * tree. Such remaining type parameters always come from an inferred PolyType.
+ */
+ public Tree mkTypeApply(Tree tree, Symbol[] tparams, Type restype, Type[] targs) {
+ Tree tree1 = tree;
+ Symbol sym = tree.symbol();
+ int i = 0;
+ while (i < tparams.length && tparams[i].owner() == sym)
+ i++;
+ if (i < tparams.length) {
+ //new Printer().print(tree1);//DEBUG
+ //System.out.println(ArrayApply.toString(targs) + "/" + i + "/" + ArrayApply.toString(tparams));//DEBUG
+ Symbol[] tparams1 = new Symbol[tparams.length - i];
+ System.arraycopy(tparams, i, tparams1, 0, tparams1.length);
+ Type[] targs1 = new Type[tparams.length - i];
+ System.arraycopy(targs, i, targs1, 0, targs1.length);
+ tree1 = substituter.apply(tree1, tparams1, targs1);
+ }
+ if (0 < i) {
+ Tree[] argtrees = new Tree[i];
+ for (int j = 0; j < i; j++)
+ argtrees[j] = gen.mkType(tree.pos, targs[j]);
+ tree1 = make.TypeApply(tree.pos, tree1, argtrees);
+ }
+ //System.out.println(Sourcefile.files[Position.file(tree1.pos)] + ": ");
+ return tree1.setType(restype.subst(tparams, targs));
+ }
+
+ /** Return the instantiated and normalized type of polymorphic expression
+ * with type `[tparams]restype', given a two prototypes `pt1', and `pt2'.
+ * `pt1' is the strict first attempt prototype where type parameters
+ * are left unchanged. `pt2' is the fall-back prototype where type parameters
+ * are replaced by `AnyType's. We try to instantiate first to `pt1' and then,
+ * if this fails, to `pt2'. If both atempts fail, a `Type.Error' is thrown.
+ */
+ Type argumentTypeInstance(Symbol[] tparams, Type restype, Type pt1, Type pt2)
+ throws Type.Error {
+ switch (restype) {
+ case PolyType(Symbol[] tparams1, Type restype1):
+ Symbol[] tparams2 = new Symbol[tparams.length + tparams1.length];
+ System.arraycopy(tparams, 0, tparams2, 0, tparams.length);
+ System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length);
+ return argumentTypeInstance(tparams2, restype1, pt1, pt2);
+ default:
+ if (tparams.length != 0) {
+ Type[] targs = instTypeArgs(tparams, restype, pt1);
+ if (targs == null)
+ targs = instTypeArgs(tparams, restype, pt2);
+ if (targs == null)
+ throw new Type.Error(
+ typeErrorMsg(
+ "polymorphic argument cannot be instantiated to formal parameter type",
+ Type.PolyType(tparams, restype), pt2));
+ checkBounds(tparams, targs, "inferred ");
+ return restype.subst(tparams, targs);
+ } else {
+ return normalize(restype, pt2);
+ }
+ }
+ }
+
+ /** Instantiate expression `tree' of polymorphic type with given `tparams' and
+ * `restype', using prototype `pt'.
+ */
+ public Tree exprInstance(Tree tree, Symbol[] tparams, Type restype, Type pt)
+ throws Type.Error {
+ switch (restype) {
+ case PolyType(Symbol[] tparams1, Type restype1):
+ Symbol[] tparams2 = new Symbol[tparams.length + tparams1.length];
+ System.arraycopy(tparams, 0, tparams2, 0, tparams.length);
+ System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length);
+ return exprInstance(tree, tparams2, restype1, pt);
+ }
+ Type[] targs = instTypeArgs(tparams, restype, pt);
+ if (targs == null)
+ throw new Type.Error(
+ "polymorphic expression of type " + tree.type +
+ " cannot be instantiated from expected type " + pt);
+ checkBounds(tparams, targs, "inferred ");
+ return mkTypeApply(tree, tparams, restype, targs);
+ }
+
+ /** Instantiate method `tree' of polymorphic type with given `tparams' and
+ * `restype', so that resulting method type can be applied to
+ * arguments with types `argtypes'.
+ */
+ public Tree methodInstance(Tree tree,
+ Symbol[] tparams, Type restype, Type[] argtypes)
+ throws Type.Error {
+ switch (restype) {
+ case PolyType(Symbol[] tparams1, Type restype1):
+ Symbol[] tparams2 = new Symbol[tparams.length + tparams1.length];
+ System.arraycopy(tparams, 0, tparams2, 0, tparams.length);
+ System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length);
+ return methodInstance(tree, tparams2, restype1, argtypes);
+ case MethodType(Symbol[] params, Type restpe):
+ Type[] argtypes1 = Type.widen(argtypes);
+ Type[] targs;
+ try {
+ targs = methTypeArgs(tparams, params, argtypes1, true);
+ } catch (NoInstance ex) {
+ throw new Type.Error(
+ applyErrorMsg(
+ "no type parameters for ", tree,
+ " exist so that it can be applied to arguments ",
+ argtypes1, Type.AnyType) +
+ "\n --- because ---\n" + ex.getMessage());
+ }
+ Symbol[] uninstantiated = normalizeArgs(targs, tparams);
+ checkBounds(tparams, targs, "inferred ");
+ Type restype1 = (uninstantiated.length == 0) ? restype
+ : Type.MethodType(params,
+ new VirtualPolyType(uninstantiated, restpe));
+ return mkTypeApply(tree, tparams, restype1, targs);
+ default:
+ return tree;
+ }
+ }
+
+ /** Instantiate constructor `tree' of polymorphic type with given `tparams' and
+ * `restype', so that its result type matches prototype `pt'.
+ */
+ public void constructorInstance(Tree tree,
+ Symbol[] tparams, Type restype, Type pt)
+ throws Type.Error {
+ switch (restype) {
+ case PolyType(Symbol[] tparams1, Type restype1):
+ Symbol[] tparams2 = new Symbol[tparams.length + tparams1.length];
+ System.arraycopy(tparams, 0, tparams2, 0, tparams.length);
+ System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length);
+ constructorInstance(tree, tparams2, restype1, pt);
+ return;
+ }
+ Type[] tvars = freshVars(tparams);
+ Type restype1 = restype.subst(tparams, tvars);
+ Type ctpe1 = restype1.resultType();
+ if (ctpe1.isSubType(pt)) {
+ Type[] targs = new Type[tparams.length];
+ for (int i = 0; i < tvars.length; i++) {
+ try {
+ targs[i] = instantiateUpper(tvars[i], true);
+ } catch (NoInstance ex) {
+ throw new Type.Error(
+ "constructor of type " + ctpe1 +
+ " can be instantiated in mode than one way to expected type " +
+ pt +
+ "\n --- because ---\n" + ex.getMessage());
+ }
+ }
+ checkBounds(tparams, targs, "inferred ");
+ tree.setType(restype.subst(tparams, targs));
+ //System.out.println("inferred constructor type: " + tree.type);//DEBUG
+ } else {
+ throw new Type.Error(
+ typeErrorMsg(
+ "constructor cannot be instantiated to expected type",
+ ctpe1, pt));
+ }
+ }
+
+// Overload Resolution -------------------------------------------------------------
+
+ /** Is function type `ftpe' applicable to `argtypes' and
+ * does its result conform to `pt'?
+ */
+ boolean isApplicable(Type ftpe, Type[] argtypes, Type pt) {
+ switch (ftpe) {
+ case MethodType(Symbol[] params, Type restpe):
+ return
+ isCompatible(restpe, pt) &&
+ params.length == argtypes.length &&
+ Type.isSubType(argtypes, Symbol.type(params));
+ case PolyType(Symbol[] tparams, MethodType(Symbol[] params, Type restpe)):
+ try {
+ Type[] targs = methTypeArgs(
+ tparams, params, Type.widen(argtypes), false);
+ if (targs != null) {
+ Symbol[] uninstantiated = normalizeArgs(targs, tparams);
+ return
+ isWithinBounds(tparams, targs) &&
+ instTypeArgs(uninstantiated, restpe.subst(tparams, targs), pt)
+ != null;
+ }
+ } catch (NoInstance ex) {
+ }
+ }
+ return false;
+ }
+
+ /** Does function type `ftpe1' specialize function type `ftpe2'
+ * when both are alternatives in an overloaded function?
+ */
+ boolean specializes(Type ftpe1, Type ftpe2) {
+ switch (ftpe1) {
+ case MethodType(Symbol[] params, _):
+ return isApplicable(ftpe2, Symbol.type(params), Type.AnyType);
+ case PolyType(_, MethodType(Symbol[] params, _)):
+ return isApplicable(ftpe2, Symbol.type(params), Type.AnyType);
+ default:
+ return false;
+ }
+ }
+
+ /** Assign `tree' the type of the alternative which matches
+ * prototype `pt', if it exists.
+ * If several alternatives match `pt', take unique parameterless one.
+ * Throw a Type.Error if several such alternatives exist.
+ * If no alternative matches, leave `tree' unchanged.
+ */
+ public void exprAlternative(Tree tree, Symbol[] alts,
+ Type[] alttypes, Type pt)
+ throws Type.Error {
+ if (alts.length == 1) {
+ tree.setSymbol(alts[0]).setType(alttypes[0]);
+ return;
+ }
+ int best = -1;
+ for (int i = 0; i < alttypes.length; i++) {
+ if (isCompatible(alttypes[i], pt) &&
+ (best < 0 || improves(alttypes[i], alttypes[best]))) {
+ best = i;
+ }
+ }
+ if (best >= 0) {
+ for (int i = 0; i < alttypes.length; i++) {
+ if (isCompatible(alttypes[i], pt) &&
+ best != i && !improves(alttypes[best], alttypes[i])) {
+ throw new Type.Error(overloadResolveErrorMsg(
+ alts[best], alttypes[best], alts[i], alttypes[i]));
+ }
+ }
+ tree.setSymbol(alts[best]).setType(alttypes[best]);
+ }
+ }
+ //where
+ private boolean improves(Type tp1, Type tp2) {
+ return !isParameterized(tp1) && isParameterized(tp2);
+ }
+
+ /** Assign `tree' the type of an alternative
+ * which is applicable to `argtypes', and whose result type is
+ * a subtype of `pt' if it exists.
+ * If several applicable alternatives exist, take the
+ * most specialized one, or throw an error if no
+ * most specialized applicable alternative exists.
+ * If no alternative matches, leave `tree' unchanged.
+ */
+ public void methodAlternative(Tree tree, Symbol[] alts, Type[] alttypes,
+ Type[] argtypes, Type pt)
+ throws Type.Error {
+ if (alts.length == 1) {
+ tree.setSymbol(alts[0]).setType(alttypes[0]);
+ return;
+ }
+ int best = -1;
+ for (int i = 0; i < alttypes.length; i++) {
+ if (isApplicable(alttypes[i], argtypes, pt) &&
+ (best < 0 || specializes(alttypes[i], alttypes[best]))) best = i;
+ }
+ if (best >= 0) {
+ for (int i = 0; i < alttypes.length; i++) {
+ if (i != best &&
+ isApplicable(alttypes[i], argtypes, pt) &&
+ !(specializes(alttypes[best], alttypes[i]) &&
+ !specializes(alttypes[i], alttypes[best]))) {
+ throw new Type.Error(overloadResolveErrorMsg(
+ alts[best], alttypes[best], alts[i], alttypes[i]));
+ }
+ }
+ tree.setSymbol(alts[best]).setType(alttypes[best]);
+ }
+ }
+
+ /** Assign `tree' the type of unique polymorphic alternative with `nparams' numbers
+ * of type parameters, if it exists.
+ * throw error if several polymorphic alternatives exist.
+ * If no alternative matches, leave `tree' unchanged.
+ */
+ public void polyAlternative(Tree tree,
+ Symbol[] alts, Type[] alttypes, int nparams)
+ throws Type.Error {
+ if (alts.length == 1) {
+ tree.setSymbol(alts[0]).setType(alttypes[0]);
+ return;
+ }
+ int i = 0;
+ while (i < alttypes.length &&
+ !(alts[i].isValue() && alttypes[i].typeParams().length == nparams)) {
+ i++;
+ }
+ if (i < alttypes.length) {
+ for (int j = i + 1; j < alttypes.length; j++) {
+ if (alts[i].isValue() && alttypes[i].typeParams().length == nparams)
+ throw new Type.Error(overloadResolveErrorMsg(
+ alts[i], alttypes[i], alts[j], alttypes[j]));
+ }
+ tree.setSymbol(alts[i]).setType(alttypes[i]);
+ }
+ }
+}
+
diff --git a/sources/scalac/typechecker/RefCheck.java b/sources/scalac/typechecker/RefCheck.java
new file mode 100644
index 0000000000..b45a9d3a2f
--- /dev/null
+++ b/sources/scalac/typechecker/RefCheck.java
@@ -0,0 +1,208 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+// todo: Any cannot be inherited.
+// todo: check that only stable defs override stable defs
+// todo: admit vardefs w/o rhs
+// ClassTemplate ::= [extends Constr {with Constr}]
+// [with `(' TemplateStatSeq `)']
+// todo: pretty printer prints wrong precedence for `new' (-> lambdalift.scala).
+
+package scalac.typechecker;
+
+import java.util.HashMap;
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import Tree.*;
+
+/** Check that no forward reference to a term symbol extends beyond a value definition.
+ */
+public class RefCheck extends Transformer implements Modifiers, Kinds {
+
+ public RefCheck(Global global, RefCheckPhase descr) {
+ super(global, descr);
+ }
+
+ private Unit unit;
+ private Scope[] scopes = new Scope[4];
+ private int[] maxindex = new int[4];
+ private int[] refpos = new int[4];
+ private Symbol[] refsym = new Symbol[4];
+ private int level;
+ private HashMap symIndex = new HashMap();
+
+ void pushLevel() {
+ level++;
+ if (level == scopes.length) {
+ Scope[] scopes1 = new Scope[scopes.length * 2];
+ int[] maxindex1 = new int[scopes.length * 2];
+ int[] refpos1 = new int[scopes.length * 2];
+ Symbol[] refsym1 = new Symbol[scopes.length * 2];
+ System.arraycopy(scopes, 0, scopes1, 0, scopes.length);
+ System.arraycopy(maxindex, 0, maxindex1, 0, scopes.length);
+ System.arraycopy(refpos, 0, refpos1, 0, scopes.length);
+ System.arraycopy(refsym, 0, refsym1, 0, scopes.length);
+ scopes = scopes1;
+ maxindex = maxindex1;
+ refpos = refpos1;
+ refsym = refsym1;
+ }
+ scopes[level] = new Scope(scopes[level - 1]);
+ maxindex[level] = Integer.MIN_VALUE;
+ }
+
+ void popLevel() {
+ scopes[level] = null;
+ level --;
+ }
+
+ public void apply(Unit unit) {
+ this.unit = unit;
+ level = 0;
+ scopes[0] = new Scope();
+ maxindex[0] = Integer.MIN_VALUE;
+ unit.body = transformStats(unit.body);
+ scopes[0] = null;
+ symIndex.clear();
+ }
+
+ /** compensate for renaming during addition of access functions
+ */
+ Name normalize(Name name) {
+ return (name.endsWith(Name.fromString("$")))
+ ? name.subName(0, name.length() - 1)
+ : name;
+ }
+
+ void enterSyms(Tree[] stats) {
+ for (int i = 0; i < stats.length; i++) {
+ enterSym(stats[i], i);
+ }
+ }
+
+ void enterSym(Tree stat, int index) {
+ Symbol sym = null;
+ switch (stat) {
+ case ClassDef(_, _, _, _, _, _):
+ sym = stat.symbol().constructor();
+ break;
+ case DefDef(_, _, _, _, _, _):
+ case ModuleDef(_, _, _, _):
+ case ValDef(_, _, _, _):
+ sym = stat.symbol();
+ }
+ if (sym != null) {
+ scopes[level].enter(sym);
+ symIndex.put(sym, new Integer(index));
+ }
+ }
+
+ public Tree[] transformStats(Tree[] stats) {
+ pushLevel();
+ enterSyms(stats);
+ int i = 0;
+ while (i < stats.length) {
+ Tree[] newstat = transformStat(stats[i], i);
+ if (newstat != null) {
+ Tree[] newstats = new Tree[stats.length + newstat.length - 1];
+ System.arraycopy(stats, 0, newstats, 0, i);
+ System.arraycopy(newstat, 0, newstats, i, newstat.length);
+ System.arraycopy(stats, i + 1, newstats, i + newstat.length,
+ stats.length - i - 1);
+ i = i + newstat.length;
+ stats = newstats;
+ } else {
+ i = i + 1;
+ }
+ }
+ popLevel();
+ return stats;
+ }
+
+ /** The main checking functions
+ */
+ public Tree[] transformStat(Tree tree, int index) {
+ Tree resultTree;
+ switch (tree) {
+ case ModuleDef(int mods, Name name, Tree tpe, Tree.Template templ):
+ Symbol sym = tree.symbol();
+ // local modules are not yet supported but the interpreter
+ // accepts modules at the console level
+ if (sym.isLocal()) unit.error("local modules are not yet supported");
+
+ Tree[] result = new Tree[2];
+ result[0] = make.ClassDef(
+ tree.pos,
+ mods | FINAL | MODUL,
+ name.toTypeName(),
+ new Tree.TypeDef[0],
+ new Tree.ValDef[][]{{}},
+ Tree.Empty,
+ templ)
+ .setSymbol(sym.moduleClass()).setType(tree.type);
+ result[1] = make.ValDef(
+ tree.pos,
+ mods | MODUL,
+ name,
+ (tpe == Tree.Empty) ? gen.mkType(tree.pos, sym.type()) : tpe,
+ gen.New(
+ tree.pos,
+ sym.type().prefix(),
+ sym.moduleClass(),
+ Tree.EMPTY_ARRAY))
+ .setSymbol(sym).setType(tree.type);
+ return transform(result);
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ Symbol sym = tree.symbol();
+ resultTree = transform(tree);
+ //todo: handle variables
+ if (index <= maxindex[level]) {
+ if (Global.instance.debug)
+ System.out.println(refsym[level] + ":" + refsym[level].type());
+ unit.error(
+ refpos[level],
+ "forward reference extends over definition of value " +
+ normalize(name));
+ }
+ break;
+ default:
+ resultTree = transform(tree);
+ }
+ return (resultTree == tree) ? null : new Tree[]{resultTree};
+ }
+
+ public Tree transform(Tree tree) {
+ switch (tree) {
+ case Template(Tree[] bases, Tree[] body):
+ Tree[] bases1 = transform(bases);
+ Tree[] body1 = transformStats(body);
+ return copy.Template(tree, bases1, body1);
+ case Block(Tree[] stats):
+ Tree[] stats1 = transformStats(stats);
+ return copy.Block(tree, stats1);
+ case Ident(Name name):
+ Scope.Entry e = scopes[level].lookupEntry(name);
+ if (tree.symbol() == e.sym) {
+ int i = level;
+ while (scopes[i] != e.owner) i--;
+ int symindex = ((Integer) symIndex.get(tree.symbol())).intValue();
+ if (maxindex[i] < symindex) {
+ refpos[i] = tree.pos;
+ refsym[i] = e.sym;
+ maxindex[i] = symindex;
+ }
+ }
+ return tree;
+ default:
+ return super.transform(tree);
+ }
+ }
+}
+
diff --git a/sources/scalac/typechecker/RefCheckPhase.java b/sources/scalac/typechecker/RefCheckPhase.java
new file mode 100644
index 0000000000..6c1fdb08a2
--- /dev/null
+++ b/sources/scalac/typechecker/RefCheckPhase.java
@@ -0,0 +1,42 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+
+package scalac.typechecker;
+
+import scalac.*;
+import scalac.ast.*;
+import scalac.checkers.*;
+
+public class RefCheckPhase extends PhaseDescriptor {
+
+ public String name() {
+ return "refcheck";
+ }
+
+ public String description () {
+ return "reference checking";
+ }
+
+ public String taskDescription () {
+ return "reference checking";
+ }
+
+ public Phase createPhase(Global global) {
+ return new RefCheck(global, this);
+ }
+
+ public Checker[] postCheckers(Global global) {
+ return new Checker[] {
+ /* todo: uncomment
+ new CheckSymbols(global),
+ new CheckTypes(global),
+ new CheckOwners(global)
+ */
+ };
+ }
+}
diff --git a/sources/scalac/util/AbstractFile.java b/sources/scalac/util/AbstractFile.java
new file mode 100644
index 0000000000..2487e07ca3
--- /dev/null
+++ b/sources/scalac/util/AbstractFile.java
@@ -0,0 +1,557 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.util;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+import java.util.jar.*;
+
+public abstract class AbstractFile {
+
+ /** separator
+ */
+ protected char separator = File.separatorChar;
+
+ /** table of all opened jar-files
+ */
+ protected static Hashtable opened = new Hashtable();
+
+ /** get name of the file
+ */
+ public abstract String getName();
+
+ /** get path of the file
+ */
+ public abstract String getPath();
+
+ /** does the file exist?
+ */
+ public abstract boolean exists();
+
+ /** is the file a directory?
+ */
+ public abstract boolean isDirectory();
+
+ /** read content of the file into a byte[] buffer
+ */
+ public abstract byte[] read() throws IOException;
+
+ /** list contents of a directory
+ */
+ public abstract String[] list() throws IOException;
+
+ /** open a new file
+ */
+ public abstract AbstractFile open(String name);
+
+ /** return an input stream for the file
+ */
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(read());
+ }
+
+ /** open file 'name' in directory 'dirname'
+ */
+ public static AbstractFile open(String dirname, String name) {
+ AbstractFile res;
+ if (dirname == null)
+ res = new PlainFile(new File(name));
+ else if (dirname.endsWith(".zip")) {
+ AbstractFile dir = (AbstractFile)opened.get(dirname);
+ if (dir == null) {
+ dir = new ZipDir(new File(dirname));
+ if (dir.isDirectory())
+ opened.put(dirname, dir);
+ }
+ res = (name == null) ? dir : dir.open(name);
+ } else if (dirname.endsWith(".jar")) {
+ AbstractFile dir = (AbstractFile)opened.get(dirname);
+ if (dir == null) {
+ dir = new JarArchive(new File(dirname));
+ if (dir.isDirectory())
+ opened.put(dirname, dir);
+ }
+ res = (name == null) ? dir : dir.open(name);
+ } else if (name == null)
+ res = new PlainFile(new File(dirname));
+ else
+ res = new PlainFile(new File(dirname, name));
+ if (!res.exists())
+ res = null;
+ return res;
+ }
+
+ /** create file given by a fully qualified name from root directory `outdir';
+ * create intermediate directories if they do not exist already
+ */
+ public static File create(File outdir, String name,
+ String suffix) throws IOException {
+ int start = 0;
+ int end = name.indexOf('.');
+ while (end >= start) {
+ outdir = new File(outdir, name.substring(start, end));
+ if (!outdir.exists())
+ outdir.mkdir();
+ start = end + 1;
+ end = name.indexOf('.', start);
+ }
+ return new File(outdir, name.substring(start) + suffix);
+ }
+}
+
+class PlainFile extends AbstractFile {
+ File f;
+
+ PlainFile(File f) {
+ this.f = f;
+ }
+
+ public String getName() {
+ return f.getName();
+ }
+
+ public String getPath() {
+ return f.getPath();
+ }
+
+ public boolean exists() {
+ return f.exists();
+ }
+
+ public boolean isDirectory() {
+ return f.isDirectory();
+ }
+
+ public byte[] read() throws IOException {
+ FileInputStream in = new FileInputStream(f);
+ int rest = (int)f.length();
+ byte[] buf = new byte[rest];
+ do {
+ int res = in.read(buf, buf.length - rest, rest);
+ if (res == -1)
+ throw new IOException("read error");
+ rest -= res;
+ } while (rest > 0);
+ in.close();
+ return buf;
+ }
+
+ public String[] list() throws IOException {
+ File[] fs = f.listFiles();
+ if (fs == null)
+ return new String[0];
+ String[] res = new String[fs.length];
+ for (int i = 0; i < fs.length; i++) {
+ res[i] = fs[i].getName();
+ if (fs[i].isDirectory() &&
+ !res[i].endsWith("/"))
+ res[i] = res[i] + "/";
+ }
+ return res;
+ }
+
+ public AbstractFile open(String name) {
+ return new PlainFile(new File(f, name));
+ }
+}
+
+class ZippedFile extends AbstractFile {
+ ZipDir dir;
+ ZipEntry zipEntry;
+
+ {
+ separator = '/';
+ }
+
+ ZippedFile(ZipDir dir, String name) {
+ this.dir = dir;
+ if (dir.zipFile != null) {
+ name = name.replace(File.separatorChar, separator);
+ zipEntry = this.dir.zipFile.getEntry(name);
+ if (zipEntry == null)
+ zipEntry = this.dir.zipFile.getEntry(name + separator);
+ }
+ }
+
+ public String getName() {
+ return zipEntry.getName();
+ }
+
+ public String getPath() {
+ return dir.getPath() + "(" + zipEntry.getName() + ")";
+ }
+
+ public boolean exists() {
+ return (zipEntry != null);
+ }
+
+ public boolean isDirectory() {
+ return zipEntry.isDirectory();
+ }
+
+ public byte[] read() throws IOException {
+ InputStream in = dir.zipFile.getInputStream(zipEntry);
+ int rest = (int)zipEntry.getSize();
+ byte[] buf = new byte[rest];
+ do {
+ int res = in.read(buf, buf.length - rest, rest);
+ if (res == -1)
+ throw new IOException("read error");
+ rest -= res;
+ } while (rest > 0);
+ in.close();
+ return buf;
+ }
+
+ public String[] list() throws IOException {
+ if (!isDirectory())
+ throw new IOException("not a directory");
+ return dir.list(zipEntry.getName());
+ }
+
+ public AbstractFile open(String name) {
+ String pathname = zipEntry.getName();
+ return new ZippedFile(dir, pathname + name);
+ }
+}
+
+class ZipDir extends AbstractFile {
+ File f;
+ ZipFile zipFile;
+
+ {
+ separator = '/';
+ }
+
+ ZipDir(File f) {
+ this.f = f;
+ try {
+ zipFile = new ZipFile(f);
+ } catch (ZipException e) {
+ } catch (IOException e) {}
+ }
+
+ public String getName() {
+ return f.getName();
+ }
+
+ public String getPath() {
+ return f.getPath();
+ }
+
+ public boolean exists() {
+ return (zipFile != null);
+ }
+
+ public boolean isDirectory() {
+ return (zipFile != null);
+ }
+
+ public byte[] read() throws IOException {
+ throw new IOException("cannot read directory");
+ }
+
+ public String[] list(String prefix) {
+ int n = 0;
+ for (Enumeration enum = zipFile.entries(); enum.hasMoreElements();) {
+ ZipEntry e = (ZipEntry)enum.nextElement();
+ if (e.getName().startsWith(prefix)) {
+ String candidate = e.getName().substring(prefix.length());
+ if (candidate.indexOf(separator) < 0)
+ n++;
+ }
+ }
+ String[] filenames = new String[n];
+ n = 0;
+ for (Enumeration enum = zipFile.entries(); enum.hasMoreElements();) {
+ ZipEntry e = (ZipEntry)enum.nextElement();
+ if (e.getName().startsWith(prefix)) {
+ String candidate = e.getName().substring(prefix.length());
+ if (candidate.indexOf(separator) < 0)
+ filenames[n++] = candidate;
+ }
+ }
+ return filenames;
+ }
+
+ public String[] list() throws IOException {
+ return list("");
+ }
+
+ public AbstractFile open(String name) {
+ return new ZippedFile(this, name);
+ }
+}
+
+final class JarArchive extends AbstractFile {
+ File f;
+ JarFile jarFile;
+ HashMap entries;
+
+ public final static String[] EMPTY = new String[0];
+
+
+ JarArchive(File f) {
+ try {
+ jarFile = new JarFile(this.f = f);
+ }
+ catch (ZipException e) {}
+ catch (IOException e) {}
+ }
+
+ public String getName() {
+ return f.getName();
+ }
+
+ public String getPath() {
+ return f.getPath();
+ }
+
+ public boolean exists() {
+ return jarFile != null;
+ }
+
+ public boolean isDirectory() {
+ return jarFile != null;
+ }
+
+ public byte[] read() throws IOException {
+ throw new IOException("cannot read archive");
+ }
+
+ private void load() {
+ entries = new HashMap();
+ if (jarFile == null)
+ return;
+ Enumeration enum = jarFile.entries();
+ while (enum.hasMoreElements()) {
+ String candidate = ((JarEntry)enum.nextElement()).getName();
+ int i = candidate.indexOf('/');
+ int j = 0;
+ HashMap files = entries;
+ while (i >= 0) {
+ String dirname = candidate.substring(j, j = (i + 1));
+ JarDirEntry dir = (JarDirEntry)files.get(dirname);
+ if (dir == null)
+ files.put(dirname, dir = new JarDirEntry(
+ candidate.substring(0, j)));
+ files = dir.entries;
+ i = candidate.indexOf('/', j);
+ }
+ if (j < (candidate.length() - 1)) {
+ String filename = candidate.substring(j);
+ JarFileEntry file = (JarFileEntry)files.get(filename);
+ if (file == null)
+ files.put(filename, new JarFileEntry(candidate));
+ }
+ }
+ }
+
+ public String[] list(String prefix) {
+ prefix = prefix.replace(File.separatorChar, '/');
+ if (entries == null)
+ load();
+ int i = prefix.indexOf('/');
+ int j = 0;
+ HashMap files = entries;
+ while (i >= 0) {
+ String dirname = prefix.substring(j, j = (i + 1));
+ JarDirEntry dir = (JarDirEntry)files.get(dirname);
+ if (dir == null)
+ return EMPTY;
+ files = dir.entries;
+ i = prefix.indexOf('/', j);
+ }
+ if (j < (prefix.length() - 1)) {
+ String filename = prefix.substring(j);
+ return (files.get(filename) != null) ? new String[]{prefix}
+ : EMPTY;
+ } else
+ return (String[])files.keySet().toArray(new String[files.size()]);
+ }
+
+ public String[] list() throws IOException {
+ return list("");
+ }
+
+ public AbstractFile open(String name) {
+ if (entries == null)
+ load();
+ name = name.replace(File.separatorChar, '/');
+ int i = name.indexOf('/');
+ int j = 0;
+ int namelen = name.length();
+ HashMap files = entries;
+ while (i >= 0) {
+ String dirname = name.substring(j, j = (i + 1));
+ if (files != null) {
+ JarDirEntry dir = (JarDirEntry)files.get(dirname);
+ if (dir == null)
+ files = null;
+ else if (j == namelen)
+ return dir;
+ else
+ files = dir.entries;
+ }
+ i = name.indexOf('/', j);
+ }
+ if (j < (namelen - 1)) {
+ String filename = name.substring(j);
+ if (files == null)
+ return new NoJarFileEntry(name);
+ JarFileEntry file = (JarFileEntry)files.get(filename);
+ if (file == null)
+ return new NoJarFileEntry(name);
+ else
+ return file;
+ } else
+ return new NoJarDirEntry(name);
+ }
+
+ static class NoJarDirEntry extends AbstractFile {
+ String name;
+
+ NoJarDirEntry(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name.substring(
+ name.lastIndexOf('/', name.length() - 2) + 1);
+ }
+
+ public String getPath() {
+ return name;
+ }
+
+ public String getFullName() {
+ return name;
+ }
+
+ public boolean exists() {
+ return false;
+ }
+
+ public boolean isDirectory() {
+ return true;
+ }
+
+ public String[] list() throws IOException {
+ throw new IOException("not a directory");
+ }
+
+ public byte[] read() throws IOException {
+ throw new IOException("cannot read archive");
+ }
+
+ public AbstractFile open(String fname) {
+ throw new Error("cannot open archive entry");
+ }
+ }
+
+ final class JarDirEntry extends NoJarDirEntry {
+ HashMap entries;
+
+ JarDirEntry(String name) {
+ super(name);
+ this.entries = new HashMap();
+ }
+
+ public String getPath() {
+ return JarArchive.this.getPath() + "(" + name + ")";
+ }
+
+ public boolean exists() {
+ return true;
+ }
+
+ public String[] list() throws IOException {
+ return JarArchive.this.list(name);
+ }
+
+ public AbstractFile open(String fname) {
+ fname = fname.replace(File.separatorChar, '/');
+ return JarArchive.this.open(name + fname);
+ }
+ }
+
+ static class NoJarFileEntry extends AbstractFile {
+ String name;
+
+ NoJarFileEntry(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name.substring(
+ name.lastIndexOf('/', name.length() - 1) + 1);
+ }
+
+ public String getFullName() {
+ return name;
+ }
+
+ public String getPath() {
+ return name;
+ }
+
+ public boolean exists() {
+ return false;
+ }
+
+ public boolean isDirectory() {
+ return false;
+ }
+
+ public String[] list() throws IOException {
+ throw new IOException("not a directory");
+ }
+
+ public byte[] read() throws IOException {
+ throw new IOException("cannot read archive");
+ }
+
+ public AbstractFile open(String fname) {
+ throw new Error("not a directory");
+ }
+ }
+
+ final class JarFileEntry extends NoJarFileEntry {
+
+ JarFileEntry(String name) {
+ super(name);
+ }
+
+ public String getPath() {
+ return JarArchive.this.getPath() + "(" + name + ")";
+ }
+
+ public boolean exists() {
+ return true;
+ }
+
+ public byte[] read() throws IOException {
+ JarEntry jarEntry = jarFile.getJarEntry(name);
+ if (jarEntry == null)
+ throw new IOException("unable to read " + name);
+ InputStream in = jarFile.getInputStream(jarEntry);
+ int rest = (int)jarEntry.getSize();
+ byte[] buf = new byte[rest];
+ do {
+ int res = in.read(buf, buf.length - rest, rest);
+ if (res == -1)
+ throw new IOException("read error");
+ rest -= res;
+ } while (rest > 0);
+ in.close();
+ return buf;
+ }
+ }
+}
diff --git a/sources/scalac/util/AbstractFileReader.java b/sources/scalac/util/AbstractFileReader.java
new file mode 100644
index 0000000000..0d18292838
--- /dev/null
+++ b/sources/scalac/util/AbstractFileReader.java
@@ -0,0 +1,107 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.util;
+
+import java.io.*;
+
+
+public class AbstractFileReader {
+
+ /** the buffer containing the file
+ */
+ public byte[] buf;
+
+ /** the current input pointer
+ */
+ public int bp;
+
+ /** constructor
+ */
+ public AbstractFileReader(AbstractFile f) throws IOException {
+ buf = f.read();
+ bp = 0;
+ }
+
+ /** return byte at offset 'pos'
+ */
+ public byte byteAt(int pos) {
+ return buf[pos];
+ }
+
+ /** read a byte
+ */
+ public byte nextByte() {
+ return buf[bp++];
+ }
+
+ /** read some bytes
+ */
+ public byte[] nextBytes(int len) {
+ byte[] res = new byte[len];
+ System.arraycopy(buf, bp, res, 0, len);
+ bp += len;
+ return res;
+ }
+
+ /** read a character
+ */
+ public char nextChar() {
+ return
+ (char)(((buf[bp++] & 0xff) << 8) +
+ (buf[bp++] & 0xff));
+ }
+
+ /** read an integer
+ */
+ public int nextInt() {
+ return ((buf[bp++] & 0xff) << 24) +
+ ((buf[bp++] & 0xff) << 16) +
+ ((buf[bp++] & 0xff) << 8) +
+ (buf[bp++] & 0xff);
+ }
+
+ /** extract a character at position bp from buf
+ */
+ public char getChar(int bp) {
+ return (char)(((buf[bp] & 0xff) << 8) + (buf[bp+1] & 0xff));
+ }
+
+ /** extract an integer at position bp from buf
+ */
+ public int getInt(int bp) {
+ return ((buf[bp ] & 0xff) << 24) +
+ ((buf[bp+1] & 0xff) << 16) +
+ ((buf[bp+2] & 0xff) << 8) +
+ (buf[bp+3] & 0xff);
+ }
+
+ /** extract a long integer at position bp from buf
+ */
+ public long getLong(int bp) {
+ return ((long)(getInt(bp)) << 32) + (getInt(bp + 4) & 0xffffffffL);
+ }
+
+ /** extract a float at position bp from buf
+ */
+ public strictfp float getFloat(int bp) {
+ return Float.intBitsToFloat(getInt(bp));
+ }
+
+ /** extract a double at position bp from buf
+ */
+ public strictfp double getDouble(int bp) {
+ return Double.longBitsToDouble(getLong(bp));
+ }
+
+ /** skip next 'n' bytes
+ */
+ public void skip(int n) {
+ bp += n;
+ }
+}
diff --git a/sources/scalac/util/ArrayApply.java b/sources/scalac/util/ArrayApply.java
new file mode 100644
index 0000000000..087278af5b
--- /dev/null
+++ b/sources/scalac/util/ArrayApply.java
@@ -0,0 +1,54 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.util;
+
+public abstract class ArrayApply {
+
+ //########################################################################
+ // Arrays interface - Object
+
+ public static String toString(Object[] src) {
+ return append(new StringBuffer(), src).toString();
+ }
+
+ public static String toString(Object[] src, String infix) {
+ return append(new StringBuffer(), src, infix).toString();
+ }
+
+ public static String toString(Object[] src, String prefix, String infix,
+ String suffix)
+ {
+ return append(new StringBuffer(), src, prefix,infix,suffix).toString();
+ }
+
+ //########################################################################
+ // Arrays interface - StringBuffer
+
+ public static StringBuffer append(StringBuffer buffer, Object[] src) {
+ return append(buffer, src, "[", ",", "]");
+ }
+
+ public static StringBuffer append(StringBuffer buffer, Object[] src,
+ String infix)
+ {
+ return append(buffer, src, "", infix, "");
+ }
+
+ public static StringBuffer append(StringBuffer buffer, Object[] src,
+ String prefix, String infix, String suffix)
+ {
+ buffer.append(prefix);
+ for (int i = 0; i < src.length; i++) {
+ if (i > 0) buffer.append(infix);
+ buffer.append(src[i]);
+ }
+ buffer.append(suffix);
+ return buffer;
+ }
+}
diff --git a/sources/scalac/util/ClassPath.java b/sources/scalac/util/ClassPath.java
new file mode 100644
index 0000000000..b083c5485b
--- /dev/null
+++ b/sources/scalac/util/ClassPath.java
@@ -0,0 +1,161 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.util;
+
+import java.io.*;
+import java.util.*;
+
+
+public class ClassPath {
+
+ /** the character separating files
+ */
+ protected static String FILE_SEP = File.separator;
+
+ /** the separator in class path specifications
+ */
+ protected static String PATH_SEP =
+ System.getProperty("path.separator");
+
+ /** the default class path
+ */
+ public static String CLASS_PATH =
+ System.getProperty("scala.class.path",
+ System.getProperty("java.class.path"));
+
+ /** the default source path
+ */
+ public static String SOURCE_PATH = null;
+
+ /** the default boot class path
+ */
+ public static String BOOT_PATH =
+ appendPath(appendPath("", System.getProperty("sun.boot.class.path")),
+ System.getProperty("scala.boot.class.path")).substring(1);
+
+ /** the default extension path
+ */
+ public static String EXTENSION_PATH =
+ System.getProperty("java.ext.dirs");
+
+ /** the various class path roots
+ */
+ protected String[] root;
+
+ /** print searches in the class path
+ */
+ public boolean printSearch;
+
+
+ /** classpath constructor
+ */
+ public ClassPath() {
+ this(CLASS_PATH);
+ }
+
+ public ClassPath(String classpath) {
+ this(classpath, SOURCE_PATH, BOOT_PATH, EXTENSION_PATH);
+ }
+
+ public ClassPath(String classpath, String sourcepath,
+ String bootclasspath, String extdirs)
+ {
+ String path = "";
+ path = appendPath(path, bootclasspath);
+ path = appendExtDirs(path, extdirs);
+ path = appendPath(path, classpath);
+ path = appendPath(path, sourcepath);
+ root = parse(path.substring(1));
+ }
+
+ /** append an additional path
+ */
+ protected static String appendPath(String path, String addpath) {
+ return addpath == null ? path : path + PATH_SEP + addpath;
+ }
+
+ /** append files from the extension directories
+ */
+ protected String appendExtDirs(String path, String extdirs) {
+ if (extdirs != null) {
+ extdirs += PATH_SEP;
+ int length = extdirs.length();
+ int i = 0;
+ while (i < length) {
+ int k = extdirs.indexOf(PATH_SEP, i);
+ String dirname = extdirs.substring(i, k);
+ String[] ext;
+ if ((dirname != null) &&
+ (dirname.length() > 0) &&
+ ((ext = new File(dirname).list()) != null)) {
+ if (!dirname.endsWith(FILE_SEP))
+ dirname += FILE_SEP;
+ for (int j = 0; j < ext.length; j++)
+ if (ext[j].endsWith(".jar") ||
+ ext[j].endsWith(".zip"))
+ path = appendPath(path, dirname + ext[j]);
+ }
+ i = k + 1;
+ }
+ }
+ return path;
+ }
+
+ /** parse a class path specification and return an array
+ * of existing class file locations
+ */
+ protected String[] parse(String path) {
+ path += PATH_SEP;
+ Vector components = new Vector();
+ int i = 0;
+ while (i < path.length()) {
+ int j = path.indexOf(PATH_SEP, i);
+ String subpath = path.substring(i, j);
+ if (new File(subpath).exists())
+ components.add(subpath);
+ i = j + 1;
+ }
+ return (String[])components.toArray(
+ new String[components.size()]);
+ }
+
+ /** find file with given name in class path and return an abstract
+ * file representation
+ */
+ public AbstractFile openFile(String name) throws FileNotFoundException {
+ if (printSearch)
+ System.out.println("looking for " + name);
+ for (int i = 0; i < root.length; i++) {
+ if (printSearch)
+ System.out.println(" in " + root[i]);
+ AbstractFile f = AbstractFile.open(root[i], name);
+ if (f != null)
+ return f;
+ }
+ throw new FileNotFoundException("file '" + name +
+ "' not found in classpath");
+ }
+
+ public String[] components() {
+ return root;
+ }
+
+ /** return a textual representation of this class path
+ */
+ public String toString() {
+ if (root.length == 0)
+ return "";
+ else if (root.length == 1)
+ return root[0];
+ String path = root[0];
+ for (int i = 1; i < root.length; i++)
+ path += PATH_SEP + root[i];
+ return path;
+ }
+}
diff --git a/sources/scalac/util/Debug.java b/sources/scalac/util/Debug.java
new file mode 100644
index 0000000000..4597463aab
--- /dev/null
+++ b/sources/scalac/util/Debug.java
@@ -0,0 +1,465 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+\* */
+
+// $Id$
+
+package scalac.util;
+
+import java.util.HashMap;
+
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+
+import scalac.Global;
+import scalac.ApplicationError;
+import scalac.symtab.Type;
+import scalac.symtab.Symbol;
+import scalac.symtab.Scope;
+
+/**
+ * Debugging class, used e.g. to obtain string representations of
+ * compiler data structures that are not "pretty-printed" and thus
+ * easier to relate to the source code.
+ *
+ * All methods are static to be easily useable in any context.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+
+public abstract class Debug {
+
+ //########################################################################
+ // Debug interface - abort
+
+ public static Error abort() {
+ return new ApplicationError();
+ }
+
+ public static Error abort(String message) {
+ return new ApplicationError(message);
+ }
+
+ public static Error abort(Object object) {
+ return new ApplicationError(object);
+ }
+
+ public static Error abort(Throwable cause) {
+ return new ApplicationError(cause);
+ }
+
+ public static Error abort(String message, Object object) {
+ return new ApplicationError(message, object);
+ }
+
+ public static Error abort(String message, Throwable cause) {
+ return new ApplicationError(message, cause);
+ }
+
+ public static Error abort(Object object, Throwable cause) {
+ return new ApplicationError(object, cause);
+ }
+
+ public static Error abort(String message, Object object, Throwable cause) {
+ return new ApplicationError(message, object, cause);
+ }
+
+ //########################################################################
+ // Debug interface - log
+
+ public static boolean log(String message) {
+ return Global.instance.log(message);
+ }
+
+ public static boolean log(String message, Object object) {
+ return log(message + ": " + show(object));
+ }
+
+ //########################################################################
+ // Debug interface - handlers
+
+ public static final HashMap/*<Class,DebugHandler>*/ handlers;
+
+ static {
+ handlers = new HashMap();
+ handlers.put(String.class, DebugToStringHandler.instance);
+ handlers.put(Type.class, DebugType.instance);
+ handlers.put(Symbol.class, DebugSymbol.instance);
+ handlers.put(Scope.class, DebugScope.instance);
+ }
+
+ public static DebugHandler getHandler(Object that) {
+ if (that == null) return DebugDefaultHandler.instance;
+ return getHandler(that.getClass());
+ }
+
+ public static DebugHandler getHandler(Class clasz) {
+ if (clasz == null) return DebugDefaultHandler.instance;
+ Object handler = handlers.get(clasz);
+ if (handler != null) return (DebugHandler)handler;
+ return getHandler(clasz.getSuperclass());
+ }
+
+ //########################################################################
+ // Debug interface - toString
+
+ public static String toString(Object that) {
+ return show(that);
+ }
+
+ //########################################################################
+ // Debug interface - show
+
+ public static String show(Object that) {
+ return show(that, getHandler(that));
+ }
+
+ public static String show(Object that, DebugHandler handler) {
+ StringBuffer buffer = new StringBuffer();
+ handler.append0(buffer, that);
+ return buffer.toString();
+ }
+
+ //########################################################################
+ // Debug interface - append
+
+ public static void append(StringBuffer buffer, Object that) {
+ getHandler(that).append0(buffer, that);
+ }
+
+ public static void appendDefault(StringBuffer buffer, Object that) {
+ if (that == null) { buffer.append("null"); return; }
+ if (!that.getClass().isArray()) {
+ appendObject(buffer, that);
+ } else {
+ appendArray(buffer, (Object[])that);
+ }
+ }
+
+ public static void appendObject(StringBuffer buffer, Object that) {
+ if (that == null) { buffer.append("null"); return; }
+ buffer.append(getClassName(that));
+ Class owner = null;
+ if (hasToString(that.getClass())) {
+ buffer.append('(');
+ buffer.append(that);
+ buffer.append(')');
+ } else {
+ int code = System.identityHashCode(that);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(code));
+ }
+ }
+
+ public static void appendArray(StringBuffer buffer, Object[] that) {
+ if (that == null) { buffer.append("null"); return; }
+ buffer.append('[');
+ for (int i = 0; i < that.length; i++) {
+ if (i > 0) buffer.append(',');
+ append(buffer, that[i]);
+ }
+ buffer.append(']');
+ }
+
+ //########################################################################
+ // Debug interface - utils
+
+ public static final Class OBJECT_CLASS = Object.class;
+
+ public static boolean hasToString(Class clasz) {
+ try {
+ Method toString = clasz.getMethod("toString", new Class[0]);
+ return toString.getDeclaringClass() != OBJECT_CLASS;
+ } catch (NoSuchMethodException excpetion) {
+ return false;
+ } catch (SecurityException excpetion) {
+ return false;
+ }
+ }
+
+ public static String getClassName(Object object) {
+ if (object == null) return "null";
+ Class clasz = object.getClass();
+ String name = clasz.getName();
+ if (!name.endsWith("$$Var")) return name;
+ Class superclass = clasz.getSuperclass();
+ Field[] fields = superclass.getDeclaredFields();
+ for (int i = 0; i < fields.length; i++) {
+ try {
+ Field field = fields[i];
+ if (field.getType() != clasz) continue;
+ if (!Modifier.isStatic(field.getModifiers())) continue;
+ Object value = field.get(null);
+ if (value != object) continue;
+ return name + "[" + field.getName() + "]";
+ } catch (IllegalAccessException exception) {
+ // continue
+ }
+ }
+ return name;
+ }
+
+ //########################################################################
+}
+
+public interface DebugHandler {
+
+ //########################################################################
+ // DebugHandler interface
+
+ public void append0(StringBuffer buffer, Object that);
+
+ //########################################################################
+}
+
+public abstract class DebugAbstractHandler implements DebugHandler {
+
+ //########################################################################
+ // DebugAbstractHandler interface
+
+ public String show(Object that) {
+ return Debug.show(that, this);
+ }
+
+ //########################################################################
+}
+
+public class DebugDefaultHandler extends DebugAbstractHandler {
+
+ //########################################################################
+ // DebugDefaultHandler interface
+
+ public static DebugDefaultHandler instance = new DebugDefaultHandler();
+
+ //########################################################################
+ // DebugHandler interface
+
+ public void append0(StringBuffer buffer, Object that) {
+ Debug.appendDefault(buffer, that);
+ }
+
+ //########################################################################
+}
+
+public class DebugToStringHandler extends DebugAbstractHandler {
+
+ //########################################################################
+ // DebugToStringHandler interface
+
+ public static DebugToStringHandler instance = new DebugToStringHandler();
+
+ //########################################################################
+ // DebugHandler interface
+
+ public void append0(StringBuffer buffer, Object that) {
+ buffer.append(that);
+ }
+
+ //########################################################################
+}
+
+public class DebugObject extends DebugAbstractHandler {
+
+ //########################################################################
+ // DebugObject interface
+
+ public static DebugObject instance = new DebugObject();
+
+ public void append1(StringBuffer buffer, Object that) {
+ Debug.appendObject(buffer, that);
+ }
+
+ //########################################################################
+ // DebugHandler interface
+
+ public void append0(StringBuffer buffer, Object that) {
+ append1(buffer, (Object)that);
+ }
+
+ //########################################################################
+}
+
+public class DebugArray extends DebugAbstractHandler {
+
+ //########################################################################
+ // DebugArray interface
+
+ public static DebugArray instance = new DebugArray();
+
+ public void append1(StringBuffer buffer, Object[] that) {
+ Debug.appendArray(buffer, that);
+ }
+
+ //########################################################################
+ // DebugHandler interface
+
+ public void append0(StringBuffer buffer, Object that) {
+ append1(buffer, (Object[])that);
+ }
+
+ //########################################################################
+}
+
+public class DebugType extends DebugAbstractHandler {
+
+ //########################################################################
+ // DebugType interface
+
+ public static DebugType instance = new DebugType();
+
+ public void append1(StringBuffer buffer, Type that) {
+ switch (that) {
+
+ case ErrorType:
+ buffer.append("ErrorType");
+ return;
+
+ case AnyType:
+ buffer.append("AnyType");
+ return;
+
+ case NoType:
+ buffer.append("NoType");
+ return;
+
+ case ThisType(Symbol symbol):
+ buffer.append("ThisType(");
+ Debug.append(buffer, symbol);
+ buffer.append(')');
+ return;
+
+ case TypeRef(Type prefix, Symbol symbol, Type[] args) :
+ buffer.append("TypeRef(");
+ Debug.append(buffer, prefix);
+ buffer.append(',');
+ Debug.append(buffer, symbol);
+ buffer.append(',');
+ Debug.append(buffer, args);
+ buffer.append(')');
+ return;
+
+ case SingleType(Type prefix, Symbol symbol):
+ buffer.append("SingleType(");
+ Debug.append(buffer, prefix);
+ buffer.append(',');
+ Debug.append(buffer, symbol);
+ buffer.append(')');
+ return;
+
+ case CompoundType(Type[] basetypes, Scope members):
+ buffer.append("CompoundType(");
+ Debug.append(buffer, basetypes);
+ buffer.append(',');
+ Debug.append(buffer, members);
+ buffer.append(')');
+ return;
+
+ case MethodType(Symbol[] vparams, Type result):
+ buffer.append("MethodType(");
+ Debug.append(buffer, vparams);
+ buffer.append(',');
+ Debug.append(buffer, result);
+ buffer.append(')');
+ return;
+
+ case PolyType(Symbol[] tparams, Type result):
+ buffer.append("PolyType(");
+ Debug.append(buffer, tparams);
+ buffer.append(',');
+ Debug.append(buffer, result);
+ buffer.append(')');
+ return;
+
+ case CovarType(Type tp):
+ buffer.append("CovarType(");
+ Debug.append(buffer, tp);
+ buffer.append(')');
+ return;
+
+ case OverloadedType(Symbol[] alts, Type[] alttypes):
+ buffer.append("OverloadedType(");
+ Debug.append(buffer, alts);
+ buffer.append(',');
+ Debug.append(buffer, alttypes);
+ buffer.append(')');
+ return;
+
+ default:
+ Debug.appendDefault(buffer, that);
+ return;
+ }
+ }
+
+ //########################################################################
+ // DebugHandler interface
+
+ public void append0(StringBuffer buffer, Object that) {
+ append1(buffer, (Type)that);
+ }
+
+ //########################################################################
+}
+
+public class DebugSymbol extends DebugAbstractHandler {
+
+ //########################################################################
+ // DebugSymbol interface
+
+ public static DebugSymbol instance = new DebugSymbol();
+
+ public void append1(StringBuffer buffer, Symbol that) {
+ if (that != Symbol.NONE && that != Symbol.ERROR) {
+ buffer.append(that.kindString()).append(' ');
+ if (that.owner() != Global.instance.definitions.ROOT_CLASS) {
+ buffer.append(that.owner().fullName());
+ buffer.append("::");
+ }
+ }
+ buffer.append(that.name);
+ if (Global.instance.uniqid) {
+ buffer.append('#');
+ buffer.append(Global.instance.uniqueID.id(that));
+ }
+ }
+
+ //########################################################################
+ // DebugHandler interface
+
+ public void append0(StringBuffer buffer, Object that) {
+ append1(buffer, (Symbol)that);
+ }
+
+ //########################################################################
+}
+
+public class DebugScope extends DebugAbstractHandler {
+
+ //########################################################################
+ // DebugScope interface
+
+ public static DebugScope instance = new DebugScope();
+
+ public void append1(StringBuffer buffer, Scope that) {
+ buffer.append('{');
+ for (Scope.SymbolIterator i = that.iterator(); i.hasNext();) {
+ Debug.append(buffer, i.next());
+ if (i.hasNext()) buffer.append(',');
+ }
+ buffer.append('}');
+ }
+
+ //########################################################################
+ // DebugHandler interface
+
+ public void append0(StringBuffer buffer, Object that) {
+ append1(buffer, (Scope)that);
+ }
+
+ //########################################################################
+}
+
diff --git a/sources/scalac/util/FreshNameCreator.java b/sources/scalac/util/FreshNameCreator.java
new file mode 100644
index 0000000000..fb69ad5c33
--- /dev/null
+++ b/sources/scalac/util/FreshNameCreator.java
@@ -0,0 +1,59 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.util;
+
+import java.util.HashMap;
+
+public class FreshNameCreator {
+
+ protected int counter = 0;
+ protected HashMap counters = new HashMap();
+
+ /**
+ * 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 with the same separator character (which
+ * has to be a non-digit).
+ */
+ public Name newName(String prefix, char separator) {
+ prefix += separator;
+ Integer ival = (Integer)counters.get(prefix);
+ if (ival == null)
+ counters.put(prefix, ival = new Integer(0));
+ else
+ counters.put(prefix, ival = new Integer(ival.intValue() + 1));
+ return Name.fromString(prefix + ival);
+ }
+
+ /** Same, with `$' as the separator character
+ */
+ public Name newName(String prefix) {
+ return newName(prefix, '$');
+ }
+
+ /** Same, but with a name as prefix. The new name is a type
+ * (respectively, constructor) name if the prefix is one.
+ */
+ public Name newName(Name prefixName, char separator) {
+ Name name = newName(prefixName.toString(), separator);
+ if (prefixName.isTypeName()) return name.toTypeName();
+ else if (prefixName.isConstrName()) return name.toConstrName();
+ else return name;
+ }
+
+ /** Same, with `$' as the separator character
+ */
+ public Name newName(Name prefix) {
+ return newName(prefix, '$');
+ }
+
+ public Name newName() {
+ return Name.fromString("$" + (counter++) + "$");
+ }
+}
diff --git a/sources/scalac/util/Name.java b/sources/scalac/util/Name.java
new file mode 100644
index 0000000000..0c04fe2dcb
--- /dev/null
+++ b/sources/scalac/util/Name.java
@@ -0,0 +1,391 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.util;
+
+public final class Name {
+
+/** address in the name memory
+ */
+ public int index;
+
+/** length of the name
+ */
+ private int len;
+
+/** next name in the same hash bucket
+ */
+ private Name next;
+
+ private final static int HASH_SIZE = 0x8000;
+ private final static int HASH_MASK = 0x7FFF;
+ private final static int NAME_SIZE = 0x20000;
+
+/** memory to store all names sequentially
+ */
+ public static byte[] names = new byte[NAME_SIZE];
+ private static int nc = 0;
+
+/** hashtable for finding term names quickly
+ */
+ private static Name[] termHashtable = new Name[HASH_SIZE];
+
+/** hashtable for finding type names quickly
+ */
+ private static Name[] typeHashtable = new Name[HASH_SIZE];
+
+/** hashtable for finding constructor names quickly
+ */
+ private static Name[] constrHashtable = new Name[HASH_SIZE];
+
+/** Constructor
+ */
+ Name(int index, int len, Name[] table, int hash) {
+ this.index = index;
+ this.len = len;
+ this.next = table[hash];
+ table[hash] = this;
+ }
+
+/** the hashcode of a name
+ */
+ private static int hashValue(byte cs[], int offset, int len) {
+ if (len > 0)
+ return
+ len * (41 * 41 * 41) +
+ cs[offset] * (41 * 41) +
+ cs[offset + len - 1] * 41 +
+ cs[offset + (len >> 1)];
+ else
+ return 0;
+ }
+
+/** is (the ascii representation of) name equal to
+ * cs[offset..offset+len-1]?
+ */
+ private static boolean equals(int index, byte cs[], int offset, int len) {
+ int i = 0;
+ while ((i < len) && (names[index + i] == cs[offset + i]))
+ i++;
+ return i == len;
+ }
+
+/** the empty name
+ */
+ public static final Name EMPTY = fromString("");
+
+/** create a term name from the bytes in cs[offset..offset+len-1].
+ * assume that bytes are in ascii format.
+ */
+ public static Name fromAscii(byte cs[], int offset, int len) {
+ int h = hashValue(cs, offset, len) & HASH_MASK;
+ Name n = termHashtable[h];
+ while ((n != null) && (n.len != len || !equals(n.index, cs, offset, len)))
+ n = n.next;
+ if (n == null) {
+ n = new Name(nc, len, termHashtable, h);
+ for (int i = 0; i < len; i++)
+ {
+ if (nc == names.length)
+ {
+ byte[] newnames = new byte[names.length * 2];
+ System.arraycopy(names, 0, newnames, 0, names.length);
+ names = newnames;
+ }
+ names[nc++] = cs[offset + i];
+ }
+ if (len == 0)
+ nc++;
+ }
+ return n;
+ }
+
+/** create a name from the bytes in cs[offset..offset+len-1];
+ * assume that characters are in source format
+ */
+ public static Name fromSource(byte cs[], int offset, int len) {
+ byte[] ascii = new byte[len * 2];
+ int alen = SourceRepresentation.source2ascii(cs, offset, len, ascii);
+ return fromAscii(ascii, 0, alen);
+ }
+
+/** create a name from the characters in string s
+ */
+ public static Name fromString(String s) {
+ byte[] source = SourceRepresentation.string2source(s);
+ return fromSource(source, 0, source.length);
+ }
+
+/** copy bytes of this name to buffer cs, starting at offset
+ */
+ public void copyAscii(byte cs[], int offset) {
+ System.arraycopy(names, index, cs, offset, len);
+ }
+
+/** return the ascii representation of this name
+ */
+ public byte[] toAscii() {
+ byte[] ascii = new byte[len];
+ System.arraycopy(names, index, ascii, 0, len);
+ return ascii;
+ }
+
+/** return the source representation of this name
+ */
+ public byte[] toSource() {
+ return SourceRepresentation.string2source(toString());
+ }
+
+/** return the string representation of this name
+ */
+ public String toString() {
+ return SourceRepresentation.ascii2string(names, index, len);
+ }
+
+/** is this name a term name?
+ */
+ public boolean isTermName() {
+ int h = hashValue(names, index, len) & HASH_MASK;
+ Name n = termHashtable[h];
+ while (n != null && n != this)
+ n = n.next;
+ return n == this;
+ }
+
+/** is this name a type name?
+ */
+ public boolean isTypeName() {
+ int h = hashValue(names, index, len) & HASH_MASK;
+ Name n = typeHashtable[h];
+ while (n != null && n != this)
+ n = n.next;
+ return n == this;
+ }
+
+/** is this name a type name?
+ */
+ public boolean isConstrName() {
+ int h = hashValue(names, index, len) & HASH_MASK;
+ Name n = constrHashtable[h];
+ while (n != null && n != this)
+ n = n.next;
+ return n == this;
+ }
+
+/** create a term name corresponding to this name
+ */
+ public Name toTermName() {
+ int h = hashValue(names, index, len) & HASH_MASK;
+ Name n = termHashtable[h];
+ while (n != null && n.index != index)
+ n = n.next;
+ if (n == null) {
+ n = new Name(index, len, termHashtable, h);
+ }
+ return n;
+ }
+
+/** create a type name corresponding to this name
+ */
+ public Name toTypeName() {
+ int h = hashValue(names, index, len) & HASH_MASK;
+ Name n = typeHashtable[h];
+ while (n != null && n.index != index)
+ n = n.next;
+ if (n == null) {
+ n = new Name(index, len, typeHashtable, h);
+ }
+ return n;
+ }
+
+/** create a constructor name corresponding to this name
+ */
+ public Name toConstrName() {
+ int h = hashValue(names, index, len) & HASH_MASK;
+ Name n = constrHashtable[h];
+ while (n != null && n.index != index)
+ n = n.next;
+ if (n == null) {
+ n = new Name(index, len, constrHashtable, h);
+ }
+ return n;
+ }
+
+/** return the string hash value of this name
+ */
+ public int hashCode() {
+ return index;
+ }
+
+/** returns the length of this name
+ */
+ public int length() {
+ return len;
+ }
+
+/** returns i'th byte of this name
+ */
+ public byte sub(int i) {
+ return names[index + i];
+ }
+
+/** returns first occurrence of byte b in this name, len if not found
+ */
+ public int pos(byte b) {
+ return pos(b, 0);
+ }
+
+/** returns first occurrence of byte b in this name from `start', len if not found
+ */
+ public int pos(byte b, int start) {
+ int i = start;
+ while (i < len && names[index + i] != b)
+ i++;
+ return i;
+ }
+
+/** returns last occurrence of byte b in this name, -1 if not found.
+ */
+ public int lastPos(byte b) {
+ int i = len - 1;
+ while (i >= 0 && names[index + i] != b)
+ i--;
+ return i;
+ }
+
+/** does this name start with prefix?
+ */
+ public boolean startsWith(Name prefix) {
+ int i = 0;
+ while ((i < prefix.len) &&
+ (i < len) &&
+ (names[index + i] == names[prefix.index + i]))
+ i++;
+ return i == prefix.len;
+ }
+
+/** does this name end with suffix?
+ */
+ public boolean endsWith(Name suffix) {
+ int i = len - 1;
+ int j = suffix.len - 1;
+ while ((j >= 0) && (i >= 0) &&
+ (names[index + i] == names[suffix.index + j])) {
+ i--;
+ j--;
+ }
+ return j < 0;
+ }
+
+/** returns the subName starting at position start, excluding position end
+ */
+ public Name subName(int start, int end) {
+ if (end < start)
+ end = start;
+ byte[] ascii = new byte[end - start];
+ System.arraycopy(names, index + start, ascii, 0, end - start);
+ return fromAscii(ascii, 0, ascii.length);
+ }
+
+/** replace all `from' characters with `to'
+ */
+ public Name replace(byte from, byte to) {
+ byte[] ascii = new byte[len];
+ copyAscii(ascii, 0);
+ for (int i = 0; i < len; i++)
+ if (ascii[i] == from) ascii[i] = to;
+ return fromAscii(ascii, 0, len);
+ }
+
+/** returns the concatenation of this name and n
+ */
+ public Name append(Name n) {
+ byte[] ascii = new byte[len + n.len];
+ copyAscii(ascii, 0);
+ n.copyAscii(ascii, len);
+ return fromAscii(ascii, 0, ascii.length);
+ }
+
+/** returns the concatenation of all names in ns
+ */
+ public static Name concat(Name ns[]) {
+ int len = 0;
+ for (int i = 0; i < ns.length; i++)
+ len = len + ns[i].len;
+ byte[] ascii = new byte[len];
+ len = 0;
+ for (int i = 0; i < ns.length; i++) {
+ ns[i].copyAscii(ascii, len);
+ len = len + ns[i].len;
+ }
+ return fromAscii(ascii, 0, len);
+ }
+
+/** is this name an operator?
+ */
+ public boolean isOperator() {
+ return !(((names[index] >= 'a') && (names[index] <= 'z')) ||
+ ((names[index] >= 'A') && (names[index] <= 'Z')) ||
+ (names[index] == '$') ||
+ (names[index] == '_'));
+ }
+
+/** is this name a variable identifier?
+ */
+ public boolean isVariable() {
+ return ((names[index] >= 'a') && (names[index] <= 'z')) ||
+ (names[index] == '_') &&
+ this != Names.null_;
+ }
+
+ public static final Name ERROR = Name.fromString("<error>");
+
+/** precedence of this name
+ */
+ public int precedence() {
+ if (this == ERROR)
+ return -1;
+ byte ch = names[index];
+ if (((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= 'a') && (ch <= 'z')))
+ return 1;
+ switch (ch) {
+ case '|':
+ return 2;
+ case '^':
+ return 3;
+ case '&':
+ return 4;
+ case '<':
+ case '>':
+ return 5;
+ case '=':
+ case '!':
+ return 6;
+ case ':':
+ return 7;
+ case '+':
+ case '-':
+ return 8;
+ case '*':
+ case '/':
+ case '%':
+ return 9;
+ default:
+ return 10;
+ }
+ }
+
+ public static final int TOP_PRECEDENCE = 11;
+
+/** is this operator left associative
+ */
+ public boolean leftAssoc() {
+ return names[index] != ':';
+ }
+}
diff --git a/sources/scalac/util/NameTransformer.java b/sources/scalac/util/NameTransformer.java
new file mode 100644
index 0000000000..c9b5e2183f
--- /dev/null
+++ b/sources/scalac/util/NameTransformer.java
@@ -0,0 +1,111 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.util;
+
+/** A name transformer for replacing operator symbols in names by predefined
+ * names of the form $opname.
+ *
+ * @author Martin Odersky, Christine Roeckl
+ * @version 1.1
+ */
+public class NameTransformer {
+
+ public static String[] operatorName = new String[128];
+
+ static {
+ operatorName['~'] = "$tilde";
+ operatorName['='] = "$eq";
+ operatorName['<'] = "$less";
+ operatorName['>'] = "$greater";
+ operatorName['!'] = "$bang";
+ operatorName['#'] = "$hash";
+ operatorName['%'] = "$percent";
+ operatorName['^'] = "$up";
+ operatorName['&'] = "$amp";
+ operatorName['|'] = "$bar";
+ operatorName['*'] = "$times";
+ operatorName['/'] = "$div";
+ operatorName['+'] = "$plus";
+ operatorName['-'] = "$minus";
+ operatorName[':'] = "$colon";
+ }
+
+ /** Replace operator symbols by corresponding "$op_name" in names.
+ */
+ public static Name encode(Name name) {
+ int i = name.index;
+ int len = i + name.length();
+ StringBuffer res = new StringBuffer();
+ while (i < len) {
+ byte c = Name.names[i++];
+ String nop = operatorName[c];
+ if (nop == null)
+ res.append((char)c);
+ else
+ res.append(nop);
+ }
+ return Name.fromString(res.toString());
+ }
+
+ /** Replace "$op_name" by corresponding operator symbols in names.
+ */
+ public static Name decode(Name name) {
+ int i = name.index;
+ int length = i + name.length();
+ StringBuffer res = new StringBuffer();
+ while (i < length) {
+ byte c = Name.names[i++];
+ if ((char)c == '$') {
+ String op = cutOut(i, length);
+ res.append(decode("$" + op));
+ i = i + op.length();
+ }
+ else res.append((char)c);
+ }
+ return Name.fromString(res.toString());
+ }
+
+ /** Decodes (a prefix of) a string.
+ */
+ public static String decode(String string) {
+ for (int i = string.length(); i > 0; i--) {
+ String prefix = string.substring(0, i);
+ String c = lookup(prefix);
+ if (c != null) {
+ String suffix = string.substring(i, string.length());
+ return c + suffix;
+ }
+ }
+ return string;
+ }
+
+ /** Cuts out the name of an operator plus succeeding characters.
+ * Does NOT return the preceeding '$' symbol.
+ */
+ private static String cutOut(int pos, int length) {
+ int i = pos; // avoid side-effects
+ StringBuffer res = new StringBuffer();
+ while (i < length) {
+ char c = (char)Name.names[i++];
+ if (c == '$') return res.toString();
+ else res.append(c);
+ }
+ return res.toString();
+ }
+
+ /** Looks up the array entry for the operator name.
+ */
+ private static String lookup(String string) {
+ for (int i = 0; i < 128; i++) {
+ if ((operatorName[i] != null) && (string.compareTo(operatorName[i]) == 0))
+ return String.valueOf((char)i);
+ }
+ return null;
+ }
+}
diff --git a/sources/scalac/util/Names.java b/sources/scalac/util/Names.java
new file mode 100644
index 0000000000..a05aad45de
--- /dev/null
+++ b/sources/scalac/util/Names.java
@@ -0,0 +1,98 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+package scalac.util;
+
+public class Names {
+
+ public static final Name ERROR = Name.ERROR;
+ public static final Name EMPTY = Name.EMPTY;
+ public static final Name WILDCARD = Name.fromString("_");
+ public static final Name COMPOUND_NAME = Name.fromString("<ct>");
+ public static final Name ANON_CLASS_NAME = Name.fromString("$class");
+
+ public static final Name _EQ = NameTransformer.encode(Name.fromString("_="));
+ public static final Name MINUS = NameTransformer.encode(Name.fromString("-"));
+ public static final Name PLUS = NameTransformer.encode(Name.fromString("+"));
+ public static final Name BANG = NameTransformer.encode(Name.fromString("!"));
+ public static final Name TILDE = NameTransformer.encode(Name.fromString("~"));
+ public static final Name EQEQ = NameTransformer.encode(Name.fromString("=="));
+ public static final Name BANGEQ = NameTransformer.encode(Name.fromString("!="));
+ public static final Name BARBAR = NameTransformer.encode(Name.fromString("||"));
+ public static final Name AMPAMP = NameTransformer.encode(Name.fromString("&&"));
+ public static final Name COLONCOLON = NameTransformer.encode(Name.fromString("::"));
+
+ public static final Name Any = Name.fromString("Any");
+ public static final Name AnyVal = Name.fromString("AnyVal");
+ public static final Name AnyRef = Name.fromString("AnyRef");
+ public static final Name Array = Name.fromString("Array");
+ public static final Name Byte = Name.fromString("Byte");
+ public static final Name Char = Name.fromString("Char");
+ public static final Name Boolean = Name.fromString("Boolean");
+ public static final Name Double = Name.fromString("Double");
+ public static final Name False = Name.fromString("False");
+ public static final Name Float = Name.fromString("Float");
+ public static final Name Function = Name.fromString("Function");
+ public static final Name Int = Name.fromString("Int");
+ public static final Name Long = Name.fromString("Long");
+ public static final Name Nil = Name.fromString("Nil");
+ public static final Name Object = Name.fromString("Object");
+ public static final Name Predef = Name.fromString("Predef");
+ public static final Name Short = Name.fromString("Short");
+ public static final Name String = Name.fromString("String");
+ public static final Name Throwable = Name.fromString("Throwable");
+ public static final Name True = Name.fromString("True");
+ public static final Name Tuple = Name.fromString("Tuple");
+ public static final Name Unit = Name.fromString("Unit");
+ public static final Name apply = Name.fromString("apply");
+ public static final Name as = Name.fromString("as");
+ public static final Name elem = Name.fromString("elem");
+ public static final Name filter = Name.fromString("filter");
+ public static final Name flatmap = Name.fromString("flatMap");
+ public static final Name foreach = Name.fromString("foreach");
+ public static final Name hashCode = Name.fromString("hashCode");
+ public static final Name is = Name.fromString("is");
+ public static final Name java = Name.fromString("java");
+ public static final Name java_lang = Name.fromString("java.lang");
+ public static final Name java_lang_Object = Name.fromString("java.lang.Object");
+ public static final Name java_lang_String = Name.fromString("java.lang.String");
+ public static final Name java_lang_Throwable = Name.fromString("java.lang.Throwable");
+ public static final Name lang = Name.fromString("lang");
+ public static final Name match = Name.fromString("match");
+ public static final Name map = Name.fromString("map");
+ public static final Name null_ = Name.fromString("null");
+ public static final Name predef = Name.fromString("predef");
+ public static final Name runtime = Name.fromString("runtime");
+ public static final Name scala = Name.fromString("scala");
+ public static final Name scala_Algebraic = Name.fromString("scala.Algebraic");
+ public static final Name scala_Any = Name.fromString("scala.Any");
+ public static final Name scala_AnyRef = Name.fromString("scala.AnyRef");
+ public static final Name scala_AnyVal = Name.fromString("scala.AnyVal");
+ public static final Name scala_Array = Name.fromString("scala.Array");
+ public static final Name scala_Boolean = Name.fromString("scala.Boolean");
+ public static final Name scala_Byte = Name.fromString("scala.Byte");
+ public static final Name scala_Case = Name.fromString("scala.Case");
+ public static final Name scala_Char = Name.fromString("scala.Char");
+ public static final Name scala_Double = Name.fromString("scala.Double");
+ public static final Name scala_Float = Name.fromString("scala.Float");
+ public static final Name scala_Function = Name.fromString("scala.Function");
+ public static final Name scala_Int = Name.fromString("scala.Int");
+ public static final Name scala_Long = Name.fromString("scala.Long");
+ public static final Name scala_Nil = Name.fromString("scala.Nil");
+ public static final Name scala_Object = Name.fromString("scala.Object");
+ public static final Name scala_Predef = Name.fromString("scala.Predef");
+ public static final Name scala_Ref = Name.fromString("scala.Ref");
+ public static final Name scala_Short = Name.fromString("scala.Short");
+ public static final Name scala_Tuple = Name.fromString("scala.Tuple");
+ public static final Name scala_Unit = Name.fromString("scala.Unit");
+ public static final Name scala_runtime = Name.fromString("scala.runtime");
+ public static final Name toString = Name.fromString("toString");
+ public static final Name this_ = Name.fromString("this");
+ public static final Name throw_ = Name.fromString("throw");
+ public static final Name update = Name.fromString("update");
+}
+
diff --git a/sources/scalac/util/OptionParser.java b/sources/scalac/util/OptionParser.java
new file mode 100644
index 0000000000..01eecb2208
--- /dev/null
+++ b/sources/scalac/util/OptionParser.java
@@ -0,0 +1,546 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.util;
+
+import java.text.Format;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import scalac.Main;
+import scalac.PhaseDescriptor;
+//import scalac.optimizer.OptimizePhase;
+
+public class CommandParser {
+
+ private final String product;
+ private final String version;
+ private final String syntax;
+ private final Reporter reporter;
+ private final List/*<ArgumentParser>*/ parsers;
+
+ public CommandParser(String product, String version, String syntax,
+ Reporter reporter)
+ {
+ this.product = product;
+ this.version = version;
+ this.syntax = syntax;
+ this.reporter = reporter;
+ this.parsers = new ArrayList();
+ }
+
+ public String product() {
+ return product;
+ }
+
+ public String version() {
+ return version;
+ }
+
+ public String syntax() {
+ return syntax;
+ }
+
+ public Reporter reporter() {
+ return reporter;
+ }
+
+ public boolean add(ArgumentParser parser) {
+ return parsers.add(parser);
+ }
+
+ public void add(int index, ArgumentParser parser) {
+ parsers.add(index, parser);
+ }
+
+ public boolean remove(ArgumentParser parser) {
+ return parsers.remove(parser);
+ }
+
+ public List parsers() {
+ return parsers;
+ }
+
+ public boolean parse(String[] args) {
+ int errors = reporter.errors();
+ for (int i = 0; i < args.length; ) {
+ for (int j = 0; j < parsers.size(); j++) {
+ ArgumentParser parser = (ArgumentParser)parsers.get(j);
+ if (parser.matches(args, i)) {
+ i = parser.consume(args, i);
+ break;
+ }
+ }
+ }
+ return reporter.errors() == errors;
+ }
+
+ public String getHelpMessage() {
+ Format format = new MessageFormat(" {0}\t {1}");
+ List options = new ArrayList(parsers.size());
+ for (int i = 0; i < parsers.size(); i++) {
+ if (!(parsers.get(i) instanceof OptionParser)) continue;
+ OptionParser parser = (OptionParser)parsers.get(i);
+ String option = parser.getHelpMessage(format);
+ if (option != null) options.add(option);
+ }
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("usage: ").append(product());
+ if (options.size() > 0) buffer.append(" <options>");
+ if (syntax != null) buffer.append(' ').append(syntax);
+ buffer.append(Strings.EOL);
+ if (options.size() > 0) {
+ buffer.append("where possible options include:");
+ buffer.append(Strings.EOL);
+ buffer.append(Strings.format(options));
+ }
+ return buffer.toString();
+ }
+
+ public void error(String message) {
+ reporter.error(product + ": " + message);
+ }
+
+ public void warning(String message) {
+ reporter.warning(product + ": " + message);
+ }
+
+ public void note(String message) {
+ reporter.note(product + ": " + message);
+ }
+}
+
+public abstract class ArgumentParser {
+
+ public final CommandParser command;
+
+ public ArgumentParser(CommandParser command) {
+ this.command = command;
+ }
+
+ public abstract boolean matches(String[] args, int index);
+ public abstract int consume(String[] args, int index);
+
+}
+
+public class UnknownArgumentParser extends ArgumentParser {
+
+ public UnknownArgumentParser(CommandParser command) {
+ super(command);
+ }
+
+ public boolean matches(String[] args, int index) {
+ return true;
+ }
+
+ public int consume(String[] args, int index) {
+ command.error("don't known what to do with '" + args[index] + "'");
+ return index + 1;
+ }
+}
+
+public class ScalaFileArgumentParser extends ArgumentParser {
+
+ public final List list;
+
+ public ScalaFileArgumentParser(CommandParser command) {
+ super(command);
+ this.list = new ArrayList();
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].endsWith(".scala");
+ }
+
+ public int consume(String[] args, int index) {
+ list.add(args[index]);
+ return index + 1;
+ }
+
+ public String[] toArray() {
+ return (String[])list.toArray(new String[list.size()]);
+ }
+}
+
+public class ScalaProgramArgumentParser extends ArgumentParser {
+
+ public String main;
+ public String[] args;
+
+ public ScalaProgramArgumentParser(CommandParser command) {
+ super(command);
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].equals("--");
+ }
+
+ public int consume(String[] args, int index) {
+ if (index + 1 < args.length) {
+ this.main = args[index + 1];
+ this.args = new String[args.length - index - 2];
+ System.arraycopy(args, index + 2, this.args, 0, this.args.length);
+ return args.length;
+ } else {
+ command.error("option --: missing module name");
+ return args.length;
+ }
+ }
+}
+
+public abstract class OptionParser extends ArgumentParser {
+
+ public final String option;
+ public final String description;
+
+ public OptionParser(CommandParser command, String option,
+ String description)
+ {
+ super(command);
+ this.option = option;
+ this.description = description;
+ }
+
+ public String getHelpSyntax() {
+ return "-" + option;
+ }
+
+ public String getHelpDescription() {
+ return description;
+ }
+
+ public void getHelpMessageArgs(List args) {
+ args.add(getHelpSyntax());
+ args.add(getHelpDescription());
+ }
+
+ public String getHelpMessage(Format format) {
+ if (description == null) return null;
+ List args = new ArrayList();
+ getHelpMessageArgs(args);
+ return format.format(args.toArray());
+ }
+
+ public void error(String message) {
+ command.error("option -" + option + ": " + message);
+ }
+
+ public void warning(String message) {
+ command.warning("option -" + option + ": " + message);
+ }
+
+ public void note(String message) {
+ command.note("option -" + option + ": " + message);
+ }
+}
+/*
+public class OptimizeOptionParser extends OptionParser {
+
+ private final OptimizePhase optimizer;
+ public boolean optimize;
+
+ public OptimizeOptionParser(CommandParser command,
+ String option, String description, OptimizePhase optimizer)
+ {
+ super(command, option, description);
+ this.optimizer = optimizer;
+ this.optimize = false;
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].equals("-" + option);
+ }
+
+ public int consume(String[] args, int index) {
+ optimizer.setOptions(args[index].substring(1 + option.length()));
+ optimize = true;
+ return index + 1;
+ }
+
+ public String getHelpSyntax() {
+ return super.getHelpSyntax() + "[:<options>]";
+ }
+}
+*/
+public class VersionOptionParser extends OptionParser {
+
+ private final String version;
+
+ public VersionOptionParser(CommandParser command,
+ String option, String description, String version)
+ {
+ super(command, option, description);
+ this.version = version;
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].equals("-" + option);
+ }
+
+ public int consume(String[] args, int index) {
+ System.out.println(version);
+ System.exit(0);
+ return index + 1;
+ }
+}
+
+public class HelpOptionParser extends OptionParser {
+
+ public HelpOptionParser(CommandParser command,
+ String option, String description)
+ {
+ super(command, option, description);
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].equals("-?") ||
+ args[index].equals("-" + option) ||
+ args[index].equals("--" + option);
+ }
+
+ public int consume(String[] args, int index) {
+ System.out.println(command.getHelpMessage());
+ System.exit(0);
+ return index + 1;
+ }
+
+ public String getHelpSyntax() {
+ return "-? " + super.getHelpSyntax();
+ }
+}
+
+public class UnknownOptionParser extends OptionParser {
+
+ public UnknownOptionParser(CommandParser command) {
+ super(command, "", null);
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].startsWith("-");
+ }
+
+ public int consume(String[] args, int index) {
+ command.error("unknown option " + args[index]);
+ return index + 1;
+ }
+}
+
+public class BooleanOptionParser extends OptionParser {
+
+ public boolean value;
+
+ public BooleanOptionParser(CommandParser command,
+ String option, String description, boolean value)
+ {
+ super(command, option, description);
+ this.value = value;
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].equals("-" + option);
+ }
+
+ public int consume(String[] args, int index) {
+ value = true;
+ return index + 1;
+ }
+}
+
+public class StringOptionParser extends OptionParser {
+
+ public String value;
+ public String argument;
+
+ public StringOptionParser(CommandParser command,
+ String option, String description, String argument, String value)
+ {
+ super(command, option, description);
+ this.argument = argument;
+ this.value = value;
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].equals("-" + option);
+ }
+
+ public int consume(String[] args, int index) {
+ if (index + 1 < args.length) {
+ value = args[index + 1];
+ return index + 2;
+ } else {
+ error("missing argument");
+ return index + 1;
+ }
+ }
+
+ public String getHelpSyntax() {
+ String syntax = super.getHelpSyntax();
+ if (argument != null) syntax = syntax + " <" + argument + ">";
+ return syntax;
+ }
+}
+
+public class PhaseSetOptionParser extends OptionParser {
+
+ private final PhaseDescriptor[] phases;
+ private final int flag;
+ private final PrefixMatcher matcher;
+
+ public PhaseSetOptionParser(CommandParser command,
+ String option, String description, PhaseDescriptor[] phases, int flag)
+ {
+ super(command, option, description);
+ this.phases = phases;
+ this.flag = flag;
+ this.matcher = new PrefixMatcher();
+ for (int i = 0; i < phases.length; i++) {
+ PhaseDescriptor phase = phases[i];
+ matcher.insert(phase.name(), phase, phase.description());
+ }
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].startsWith("-" + option + ":");
+ }
+
+ public int consume(String[] args, int index) {
+ StringTokenizer tokens = new StringTokenizer(
+ args[index].substring(option.length() + 2), ",");
+ while (tokens.hasMoreTokens()) consumePhase(tokens.nextToken());
+ return index + 1;
+ }
+
+ public void consumePhase(String token) {
+ if (token.equals("all")) {
+ for (int i = 0; i < phases.length; i++) phases[i].flags |= flag;
+ return;
+ }
+ PhaseDescriptor phase = lookup(getPhaseName(token));
+ if (phase != null) {
+ boolean before = getBeforeFlag(token);
+ boolean after = getAfterFlag(token) || !before;
+ if (before) phase.flags |= flag << 16;
+ if (after) phase.flags |= flag;
+ }
+ }
+
+ public PhaseDescriptor lookup(String name) {
+ if (name.length() == 0) {
+ error("illegal zero-length phase name");
+ return null;
+ }
+ PrefixMatcher.Entry[] entries = matcher.lookup(name);
+ if (entries.length == 1) return (PhaseDescriptor)entries[0].value;
+ error(matcher.getErrorMessage(name, entries, "phase name"));
+ return null;
+ }
+
+ public boolean getBeforeFlag(String token) {
+ for (int i = token.length(); 0 < i--; ) {
+ switch (token.charAt(i)) {
+ case '-': return true;
+ case '+': continue;
+ default : return false;
+ }
+ }
+ return false;
+ }
+
+ public boolean getAfterFlag(String token) {
+ for (int i = token.length(); 0 < i--; ) {
+ switch (token.charAt(i)) {
+ case '-': continue;
+ case '+': return true;
+ default : return false;
+ }
+ }
+ return false;
+ }
+
+ public String getPhaseName(String token) {
+ for (int i = token.length(); 0 < i--; ) {
+ switch (token.charAt(i)) {
+ case '-': continue;
+ case '+': continue;
+ default : return token.substring(0, i + 1);
+ }
+ }
+ return "";
+ }
+
+ public String getHelpSyntax() {
+ return super.getHelpSyntax() + ":<phases>";
+ }
+}
+
+public class PrintOptionParser extends PhaseSetOptionParser {
+
+ public boolean tokens;
+
+ public PrintOptionParser(CommandParser command,
+ String option, String description, PhaseDescriptor[] phases, int flag)
+ {
+ super(command, option, description, phases, flag);
+ this.tokens = false;
+ }
+
+ public void consumePhase(String token) {
+ if ("tokens".equals(token))
+ tokens = true;
+ else
+ super.consumePhase(token);
+ }
+
+}
+
+public class ChoiceOptionParser extends OptionParser {
+
+ public final String argument;
+ public final String[] choices;
+
+ public String value;
+
+ public ChoiceOptionParser(CommandParser command,
+ String option, String description, String argument, String[] choices,
+ String value)
+ {
+ super(command, option, description);
+ this.argument = argument;
+ this.choices = choices;
+ this.value = value;
+ }
+
+ public boolean matches(String[] args, int index) {
+ return args[index].startsWith("-" + option + ":");
+ }
+
+ public int consume(String[] args, int index) {
+ String choice = args[index].substring(option.length() + 2);
+ boolean found = false;
+ for (int i = 0; i < choices.length; i++) {
+ if (choices[i].equals(choice)) { found = true; break; }
+ }
+ if (found) {
+ value = choice;
+ } else if (choice.length() > 0) {
+ error("unknown " + argument + " '" + choice + "'");
+ } else {
+ error("missing " + argument);
+ }
+ return index + 1;
+ }
+
+ public String getHelpSyntax() {
+ String syntax = super.getHelpSyntax();
+ if (argument != null) syntax = syntax + ":<" + argument + ">";
+ return syntax;
+ }
+}
diff --git a/sources/scalac/util/Position.java b/sources/scalac/util/Position.java
new file mode 100644
index 0000000000..c2cd7f72d3
--- /dev/null
+++ b/sources/scalac/util/Position.java
@@ -0,0 +1,54 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.util;
+
+
+public final class Position {
+
+/** source file positions are integers in the format:
+ * line-number << LINESHIFT + column-number
+ * NOPOS represents an undefined position.
+ */
+ public static final int LINESHIFT = 10;
+ public static final int FILESHIFT = 26;
+ public static final int COLUMNMASK = 1023;
+ public static final int LINEMASK = 0xffff;
+
+/** predefined positions
+ */
+ public static final int NOPOS = 0;
+
+/** first position in a source file
+ */
+ public static final int FIRSTPOS = (1 << LINESHIFT) + 1;
+
+/** encode a line and column number into a single int
+ */
+ public static int encode(int line, int col, int file) {
+ return (file << FILESHIFT) | (line << LINESHIFT) | col;
+ }
+
+/** get the file id of an encoded position
+ */
+ public static int file(int pos) {
+ return pos >>> FILESHIFT;
+ }
+
+/** get the line number out of an encoded position
+ */
+ public static int line(int pos) {
+ return (pos >>> LINESHIFT) & LINEMASK;
+ }
+
+/** return the column number of an encoded position
+ */
+ public static int column(int pos) {
+ return pos & COLUMNMASK;
+ }
+}
diff --git a/sources/scalac/util/PrefixMatcher.java b/sources/scalac/util/PrefixMatcher.java
new file mode 100644
index 0000000000..1b0acd8d7a
--- /dev/null
+++ b/sources/scalac/util/PrefixMatcher.java
@@ -0,0 +1,114 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.util;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+public class PrefixMatcher {
+
+ public static class Entry {
+
+ private Entry prev;
+ private Entry next;
+
+ public final String key;
+ public final Object value;
+ public final String argument;
+ public final String description;
+
+ public Entry(String key, Object value, String argument,
+ String description)
+ {
+ this.key = key;
+ this.value = value;
+ this.argument = argument;
+ this.description = description;
+ }
+ }
+
+ private final Map entries;
+ private Entry first;
+ private Entry last;
+
+ public PrefixMatcher() {
+ this.entries = new HashMap();
+ }
+
+ public void insert(String key, Object value) {
+ insert(key, value, null, null);
+ }
+
+ public void insert(String key, Object value, String description) {
+ insert(key, value, null, description);
+ }
+
+ public void insert(String key, Object value, String argument,
+ String description)
+ {
+ assert key != null && !entries.containsKey(key) : key;
+ Entry entry = new Entry(key, value, argument, description);
+ if (first == null) {
+ first = last = entry;
+ } else {
+ last.next = entry;
+ entry.prev = last;
+ last = entry;
+ }
+ entries.put(key, entry);
+ }
+
+ public Entry[] lookup(String key) {
+ Object value = entries.get(key);
+ if (value != null) return new Entry[] { (Entry)value };
+ List list = new ArrayList();
+ for (Entry i = first; i != null; i = i.next) {
+ if (i.key.startsWith(key)) list.add(i);
+ }
+ return (Entry[])list.toArray(new Entry[list.size()]);
+ }
+
+ public String getErrorMessage(String key, Entry[] entries, String what) {
+ switch (entries.length) {
+ case 0:
+ return "unknown " + what + " '" + key + "'";
+ case 1:
+ return null;
+ case 2:
+ return "ambigous " + what + " '" + key + "', both '" +
+ entries[0].key + "' and '" + entries[1].key + "' match";
+ default:
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("ambigous ").append(what);
+ buffer.append(" '").append(key).append("'");
+ for (int i = 0; i < entries.length; i++) {
+ buffer.append(i < entries.length - 1 ? ", " : " and ");
+ buffer.append('\'').append(entries[i].key).append('\'');
+ }
+ buffer.append(" match");
+ return buffer.toString();
+ }
+ }
+
+ public List getHelpStrings(String separator1, String separator2) {
+ List strings = new ArrayList();
+ for (Entry entry = first; entry != null; entry = entry.next) {
+ if (entry.description != null)
+ if (entry.argument != null)
+ strings.add(entry.key + separator1 + entry.argument +
+ separator2 + entry.description);
+ else
+ strings.add(entry.key + separator2 + entry.description);
+ }
+ return strings;
+ }
+
+}
diff --git a/sources/scalac/util/Reporter.java b/sources/scalac/util/Reporter.java
new file mode 100644
index 0000000000..a1fb832e28
--- /dev/null
+++ b/sources/scalac/util/Reporter.java
@@ -0,0 +1,192 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.util;
+
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.PrintWriter;
+import java.io.IOException;
+import scalac.ApplicationError;
+
+public class Reporter {
+
+ //########################################################################
+ // Private state
+
+ private final BufferedReader reader;
+ private final PrintWriter writer;
+
+ /** Number of errors issued totally */
+ private int errors;
+ /** Number of warning issued totally */
+ private int warnings;
+ /** Number of notes issued totally */
+ private int notes;
+
+ //########################################################################
+ // Reporter constructors
+
+ public Reporter() {
+ this(
+ new BufferedReader(new InputStreamReader(System.in)),
+ new PrintWriter(System.err, true));
+ }
+
+ public Reporter(BufferedReader reader, PrintWriter writer) {
+ this.reader = reader;
+ this.writer = writer;
+ this.prompt = false;
+ this.nowarn = false;
+ this.verbose = false;
+ this.errors = 0;
+ this.notes = 0;
+ }
+
+ //########################################################################
+ // Reporter state
+
+ /** Whether warnings should be issued */
+ public boolean nowarn;
+ /** Whether notes and information messages should be issued */
+ public boolean verbose;
+ /** Whether a prompt should be displayed after errors and warnings */
+ public boolean prompt;
+
+ //########################################################################
+ // Reporter interface - query
+
+ /** Return the number of errors issued totally */
+ public int errors() {
+ return errors;
+ }
+
+ /** Return the number of warnings issued totally */
+ public int warnings() {
+ return warnings;
+ }
+
+ /** Return the number of notes issued totally */
+ public int notes() {
+ return notes;
+ }
+
+ /** Return the number of errors issued totally as a string */
+ public String getErrorCountString() {
+ return getCountString(errors, "error");
+ }
+
+ /** Return the number of warnings issued totally as a string */
+ public String getWarningCountString() {
+ return getCountString(warnings, "warning");
+ }
+
+ /** Return the number of notes issued totally as a string */
+ public String getNoteCountString() {
+ return getCountString(notes, "note");
+ }
+
+ public String getCountString(int count, String what) {
+ switch (count) {
+ case 0: return "no " + what + "s";
+ case 1: return "one " + what;
+ case 2: return "two " + what + "s";
+ case 3: return "three " + what + "s";
+ case 4: return "four " + what + "s";
+ default: return count + " " + what + "s";
+ }
+ }
+
+ //########################################################################
+ // Reporter interface - report
+
+ /** Reset all counters */
+ public void resetCounters() {
+ errors = 0;
+ warnings = 0;
+ notes = 0;
+ }
+
+ /** Issue a message */
+ public void report(String message) {
+ writer.println(message);
+ }
+
+ /** Issue a message */
+ public void inform(String message) {
+ if (verbose) report(message);
+ }
+
+ /** Issue an error */
+ public void error(String message) {
+ error(message, false);
+ }
+
+ /** Issue an error if it is not hidden */
+ public void error(String message, boolean hidden) {
+ if (!hidden || prompt) report(message);
+ if (!hidden) errors++;
+ if (prompt) failOnDemand();
+ }
+
+ /** Issue a warning */
+ public void warning(String message) {
+ warning(message, false);
+ }
+
+ /** Issue a warning if it is not hidden */
+ public void warning(String message, boolean hidden) {
+ if (nowarn) return;
+ if (!hidden || prompt) report(message);
+ if (!hidden) warnings++;
+ if (prompt) failOnDemand();
+ }
+
+ /** Issue a note */
+ public void note(String message) {
+ note(message, false);
+ }
+
+ /** Issue a note if it is not hidden */
+ public void note(String message, boolean hidden) {
+ if (!hidden) report(message);
+ if (!hidden) notes++;
+ }
+
+ public void printSummary() {
+ if (errors() > 0) report(getErrorCountString() + " found");
+ if (warnings() > 0) report(getWarningCountString() + " found");
+ if (notes() > 0) report(getNoteCountString() + " found");
+ }
+
+ //########################################################################
+ // Reporter interface - fail
+
+ /** Fail only if requested */
+ public void failOnDemand() {
+ failOnDemand("user abort");
+ }
+
+ /** Fail only if requested */
+ public void failOnDemand(String message) {
+ try {
+ while (true) {
+ writer.print("r)esume, a)bort: ");
+ String line = reader.readLine();
+ if (line == null) continue; else line = line.toLowerCase();
+ if ("abort".startsWith(line))
+ throw new ApplicationError(message);
+ if ("resume".startsWith(line)) return;
+ }
+ } catch (IOException e) {
+ throw new ApplicationError("input read error");
+ }
+ }
+
+ //########################################################################
+}
diff --git a/sources/scalac/util/SourceRepresentation.java b/sources/scalac/util/SourceRepresentation.java
new file mode 100644
index 0000000000..7c6ee44107
--- /dev/null
+++ b/sources/scalac/util/SourceRepresentation.java
@@ -0,0 +1,205 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id
+\* */
+
+package scalac.util;
+
+public final class SourceRepresentation {
+
+ public static int digit2int(byte ch, int base) {
+ if ('0' <= ch && ch <= '9' && ch < '0' + base)
+ return ch - '0';
+ else if ('A' <= ch && ch < 'A' + base - 10)
+ return ch - 'A' + 10;
+ else if ('a' <= ch && ch < 'a' + base - 10)
+ return ch - 'a' + 10;
+ else
+ return -1;
+ }
+
+ public static byte int2digit(int x) {
+ if (x <= 9)
+ return (byte)(x + '0');
+ else
+ return (byte)(x - 10 + 'A');
+ }
+
+/* the next 4 functions convert between three fundamental name
+ * representations:
+ * - string each character 16 bit,
+ * - source characters outside 0..127 are represented by
+ * unicode escapes, \ u X X X X
+ * - ascii characters outside 0..127 are represented by two or three
+ * byte sequences with high bit set (as in class file format).
+ */
+
+/** convert source bytes in source[offset..offset+len-1] to ascii.
+ */
+ public static int source2ascii(byte source[], int offset, int len, byte ascii[]) {
+ int j = 0;
+ int i = 0;
+ while (i < len) {
+ if (source[offset + i] == '\\' && i + 1 < len) {
+ i++;
+ switch (source[offset + i]) {
+ case 'n':
+ ascii[j++] = (byte)'\n'; i++; continue;
+ case 't':
+ ascii[j++] = (byte)'\t'; i++; continue;
+ case 'b':
+ ascii[j++] = (byte)'\b'; i++; continue;
+ case 'r':
+ ascii[j++] = (byte)'\r'; i++; continue;
+ case 'f':
+ ascii[j++] = (byte)'\f'; i++; continue;
+ case 'u':
+ if (i + 4 < len) {
+ int code = 0;
+ int k = 1;
+ int d = 0;
+ while (k <= 4 && d >= 0) {
+ d = digit2int(source[offset + i + k], 16);
+ code = code * 16 + d;
+ k++;
+ }
+ if (d >= 0) {
+ if (code <= 0x7F)
+ ascii[j++] = (byte)code;
+ else
+ if (code <= 0x3FF) {
+ ascii[j++] = (byte)(0xC0 | (code >> 6));
+ ascii[j++] = (byte)(0x80 | (code & 0x3F));
+ } else {
+ ascii[j++] = (byte)(0xE0 | (code >> 12));
+ ascii[j++] = (byte)(0x80 |
+ ((code >> 6) & 0x3F));
+ ascii[j++] = (byte)(0x80 | (code & 0x3F));
+ }
+ i = i + 5;
+ continue;
+ }
+ }
+ }
+ }
+ byte b = source[offset + i++];
+ if (b >= 0)
+ ascii[j++] = b;
+ else {
+ ascii[j++] = (byte)(0xC0 | ((b >> 6) & 0x3));
+ ascii[j++] = (byte)(0x80 | (b & 0x3F));
+ }
+ }
+ return j;
+ }
+
+/** convert ascii bytes in ascii[offset..offset+len-1] to a string
+ */
+ public static String ascii2string(byte ascii[], int offset, int len) {
+ char cs[] = new char[len];
+ int i = offset;
+ int j = 0;
+ len += offset;
+ while (i < len) {
+ int b = ascii[i++] & 0xFF;
+ if (b >= 0xE0) {
+ b = ((b & 0x0F) << 12) | (ascii[i++] & 0x3F) << 6;
+ b = b | (ascii[i++] & 0x3F);
+ }
+ else
+ if (b >= 0xC0)
+ b = ((b & 0x1F) << 6) | (ascii[i++] & 0x3F);
+ cs[j++] = (char)b;
+ }
+ return new String(cs, 0, j);
+ }
+
+/** convert string to array of source bytes
+ */
+ public static byte[] string2source(String s) {
+ byte[] source = new byte[s.length() * 6];
+ int j = 0;
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ switch (ch) {
+ case '\n':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'n';
+ break;
+ case '\t':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'t';
+ break;
+ case '\b':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'b';
+ break;
+ case '\r':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'r';
+ break;
+ case '\f':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'f';
+ break;
+ case '\"':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'\"';
+ break;
+ case '\'':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'\'';
+ break;
+ case '\\':
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'\\';
+ break;
+ default:
+ if ((' ' <= ch) && (ch <= 127))
+ source[j++] = (byte)ch;
+ else {
+ source[j++] = (byte)'\\';
+ source[j++] = (byte)'u';
+ source[j++] = int2digit((ch >> 12) & 0xF);
+ source[j++] = int2digit((ch >> 8) & 0xF);
+ source[j++] = int2digit((ch >> 4) & 0xF);
+ source[j++] = int2digit(ch & 0xF);
+ }
+ }
+ }
+ byte[] res = new byte[j];
+ System.arraycopy(source, 0, res, 0, j);
+ return res;
+ }
+
+/** convert string to array of ascii bytes
+ */
+ public static byte[] string2ascii(String s) {
+ byte[] source = string2source(s);
+ byte[] ascii = new byte[source.length * 2];
+ int alen = source2ascii(source, 0, source.length, ascii);
+ byte[] res = new byte[alen];
+ System.arraycopy(ascii, 0, res, 0, alen);
+ return res;
+ }
+
+/** escape all characters outside 32..127 in string s
+ */
+ public static String escape(String s) {
+ try {
+ return new String(string2source(s), "8859_1");
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new InternalError(e.getMessage());
+ }
+ }
+
+/** escape character c, if outside 32..127.
+ */
+ public static String escape(char c) {
+ char[] s = {c};
+ return escape(new String(s));
+ }
+}
diff --git a/sources/scalac/util/Strings.java b/sources/scalac/util/Strings.java
new file mode 100644
index 0000000000..540a61020e
--- /dev/null
+++ b/sources/scalac/util/Strings.java
@@ -0,0 +1,102 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scalac.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public abstract class Strings {
+
+ //########################################################################
+ // Strings constants
+
+ final public static String[] NONE = new String[0];
+
+ //########################################################################
+ // Strings interface
+
+ /** The line separator */
+ public static String EOL = System.getProperty("line.separator", "\n");
+
+ /** Returns a string where all tabs have been replaced by white
+ * spaces to make the corresponding fields the same width.
+ */
+ public static String format(List strings) {
+ List[] lines = new List[strings.size()];
+ List widths = new ArrayList();
+ int height = 0;
+ for (Iterator iterator = strings.iterator(); iterator.hasNext(); ) {
+ String string = (String)iterator.next();
+ List line = lines[height++] = new ArrayList();
+ for (int last = 0; last < string.length(); ) {
+ int next = string.indexOf('\t', last);
+ if (next < 0) next = string.length();
+ String substring = string.substring(last, next);
+ int index = line.size();
+ if (index == widths.size()) widths.add(new Integer(0));
+ int width = ((Integer)widths.get(index)).intValue();
+ widths.set(
+ index, new Integer(Math.max(width, substring.length())));
+ line.add(substring);
+ last = next + 1;
+ }
+ }
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < lines.length; i++) {
+ List line = lines[i];
+ for (int j = 0; true; j++) {
+ String string = (String)line.get(j);
+ buffer.append(string);
+ if (j == line.size() - 1) break;
+ int width = ((Integer)widths.get(j)).intValue();
+ for (int k = string.length(); k<width; k++) buffer.append(' ');
+ }
+ buffer.append(EOL);
+ }
+ return buffer.toString();
+ }
+
+ /** Returns the first char of the string or -1 if the string is empty. */
+ public static int firstChar(String string) {
+ return string.length() == 0 ? -1 : string.charAt(0);
+ }
+
+ /** Returns the last char of the string or -1 if the string is empty. */
+ public static int lastChar(String string) {
+ return string.length() == 0 ? -1 : string.charAt(string.length() - 1);
+ }
+
+ /** Returns a copy of the string, with leading whitespace omitted. */
+ public static String trimLeading(String string) {
+ for (int i = 0; i < string.length(); i++)
+ if (string.charAt(i) > ' ') return string.substring(i);
+ return "";
+ }
+
+ /** Returns a copy of the string, with trailing whitespace omitted. */
+ public static String trimTrailing(String string) {
+ for (int i = string.length() - 1; i >= 0; i--)
+ if (string.charAt(i) > ' ') return string.substring(0, i + 1);
+ return "";
+ }
+
+ /** Returns the stack trace of the exception */
+ public static String stackTrace(Throwable exception) {
+ StringWriter buffer = new StringWriter();
+ PrintWriter writer = new PrintWriter(buffer);
+ exception.printStackTrace(writer);
+ writer.close();
+ return buffer.toString();
+ }
+
+ //########################################################################
+}
diff --git a/sources/scalac/util/UniqueID.java b/sources/scalac/util/UniqueID.java
new file mode 100644
index 0000000000..59e31a9de6
--- /dev/null
+++ b/sources/scalac/util/UniqueID.java
@@ -0,0 +1,30 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+\* */
+
+// $Id$
+
+package scalac.util;
+
+import java.util.*;
+
+/**
+ * Class to assign unique and small numbers to objects, based on their
+ * identity.
+ *
+ * @author Michel Schinz
+ * @version 1.0
+ */
+
+public class UniqueID {
+ protected Map ids = new HashMap();
+
+ public int id(Object obj) {
+ if (! ids.containsKey(obj))
+ ids.put(obj, new Integer(ids.size()));
+ return ((Integer)ids.get(obj)).intValue();
+ }
+}